Multiple Linear Regression Model to Predict Electricity Demand of Five Cities in Spain

Install all Packages and Import Library

Packages installation

# install.packages("dplyr")
# install.packages('car')
# install.packages("ggplot2")

Import library

#load package here
library(tidyverse)
library(gridBase)
library(grid)
library(dplyr)
library(car)
library (GGally)
library(readxl) 
library(ggplot2)
library(broom)
library(ggpubr)
library(corrplot)
library(MASS)
library(Stat2Data)
library(olsrr)
library(leaps)
library(leaps)
library(caret)
library(lmtest)
library(nortest)
library(Metrics)
library(broom.mixed)
library(ggbreak)
library(gridExtra)
library(tinytex)
library(randomForest) 
library(ranger)
options(scipen=999)  # turn-off scientific notation like 1e+48
theme_set(theme_bw())  # pre-set the bw theme.

Data Processing

Load weather data

## Read raw data: weather features
weather_raw<-read.csv(file = "Data/weather_features.csv")
head(weather_raw,4) 
# All the columns in the raw data
weather_raw_fields <- colnames(weather_raw) 
print(weather_raw_fields)
 [1] "dt_iso"              "city_name"           "temp"                "temp_min"            "temp_max"            "pressure"           
 [7] "humidity"            "wind_speed"          "wind_deg"            "rain_1h"             "rain_3h"             "snow_3h"            
[13] "clouds_all"          "weather_id"          "weather_main"        "weather_description" "weather_icon"       

Remove all duplicate data from the weather data

# All the duplicate data based on date 
weather_duplicate<-weather_raw[duplicated(weather_raw[,1:2]),]
weather <- weather_raw[!duplicated(weather_raw[,1:2]), ]

Load energy data set modified from Kagel

Calculation:

(Keith,Can you explain here in bullets how new excel sheet is generated in bullet?) * Use $$ for math equation, if any and reference the original data, and other links like I did in the beginning of this code

energy_raw<-read_excel("Data/energy_dataset-KO.xlsx")
New names:
  • Five cities are:
    • Valencia
    • Barcelona
    • Bilbao
    • Seville
    • Madrid

Merge Energy Demand and Weather to one data set based on cities

# Valencia
Weather_Valencia <- weather[weather$city_name == 'Valencia',]
Data_Valencia <-merge(Weather_Valencia, energy_raw[,c("time","Valencia")], by.x = "dt_iso", by.y = "time")
colnames(Data_Valencia)[colnames(Data_Valencia) == "Valencia"] <- "energy"

# Barcelona
Weather_Barcelona <- weather[weather$city_name == " Barcelona",]
Data_Barcelona <-merge(Weather_Barcelona, energy_raw[,c("time","Barcelona")], by.x = "dt_iso", by.y = "time")
colnames(Data_Barcelona)[colnames(Data_Barcelona) == "Barcelona"] <- "energy"

# Bilbao 
Weather_Bilbao <- weather[weather$city_name == 'Bilbao',]
Data_Bilbao <-merge(Weather_Bilbao, energy_raw[,c("time","Bilboa")], by.x = "dt_iso", by.y = "time")
colnames(Data_Bilbao)[colnames(Data_Bilbao) == "Bilboa"] <- "energy"
  
# Seville
Weather_Seville <- weather[weather$city_name == 'Seville',]
Data_Seville <-merge(Weather_Seville, energy_raw[,c("time","Seville")], by.x = "dt_iso", by.y = "time")
colnames(Data_Seville)[colnames(Data_Seville) == "Seville"] <- "energy"

# Madrid
Weather_Madrid <- weather[weather$city_name == 'Madrid',]
Data_Madrid <-merge(Weather_Madrid, energy_raw[,c("time","Madrid")], by.x = "dt_iso", by.y = "time")
colnames(Data_Madrid)[colnames(Data_Madrid) == "Madrid"] <- "energy"

Write all the data in separate cityname.csv sheets

write.csv(Data_Barcelona,"Data/Barcelona.csv", row.names = TRUE)
write.csv(Data_Valencia,"Data/Valencia.csv", row.names = TRUE)
write.csv(Data_Bilbao,"Data/Bilbao.csv", row.names = TRUE)
write.csv(Data_Seville,"Data/Seville.csv", row.names = TRUE)
write.csv(Data_Madrid,"Data/Madrid.csv", row.names = TRUE)

BARCELONA DATA

# Data with all predictors and response variable 
## Read raw data: Barcelona
Barcelona_raw<-read.csv(file = "Data/Barcelona.csv")

# Rename column names 
colnames(Barcelona_raw)[colnames(Barcelona_raw) == "X"] <- "DataID"
colnames(Barcelona_raw)[colnames(Barcelona_raw) == "dt_iso"] <- "time"

# Select only the columns of interest

Barcelona_Data<-Barcelona_raw %>% 
  dplyr::select(DataID,time,temp,humidity,pressure,wind_speed,rain_1h,rain_3h,snow_3h,weather_main,energy)

See all the weather description in weather_main

print(unique(Barcelona_Data$weather_main))
[1] "clear"        "clouds"       "rain"         "snow"         "drizzle"      "thunderstorm" "mist"         "fog"          "dust"        

Give integer values for each description

Barcelona_Data<-transform(Barcelona_Data, weather_main = factor(weather_main, 
      levels = c("clear", "clouds", "drizzle","dust","fog","haze","mist","rain","smoke","snow","squall","thunderstorm"),
       labels = c(1:12)))

Weather_description <- data.frame (Weather_Description  = c("clear", "clouds", "drizzle",
              "dust","fog","haze","mist","rain","smoke","snow","squall","thunderstorm"),
                  Index = c(1:12)
                  )
print(Weather_description)

More Data Processing

# Add two columns of rain duration
Barcelona_Data["rain_duration"] <- Barcelona_Data$rain_1h + Barcelona_Data$rain_3h 

# Rename Column Names for clarity 
Barcelona_Data <-merge(Barcelona_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)")], by.x = "time", by.y = "time")
colnames(Barcelona_Data )[colnames(Barcelona_Data ) == "Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)"] <- "time_band"

Barcelona_Data <-merge(Barcelona_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)")], by.x = "time", by.y = "time")
colnames(Barcelona_Data )[colnames(Barcelona_Data ) == "Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)"] <- "season"

Barcelona_Data <-merge(Barcelona_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime of day (Day vs Night)")], by.x = "time", by.y = "time")
colnames(Barcelona_Data )[colnames(Barcelona_Data ) == "Specified Categorical Variable:\r\nTime of day (Day vs Night)"] <- "day_night"


colnames(Barcelona_Data )[colnames(Barcelona_Data ) == "snow_3h"] <- "snow_duration"

Barcelona_Data<-subset(Barcelona_Data,select=-c(rain_1h,rain_3h,DataID))

colnames(Barcelona_Data )[colnames(Barcelona_Data ) == "time"] <- "time_ID"

Barcelona_Data <- Barcelona_Data [, c("time_ID","temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy")]

colnames(Barcelona_Data )[colnames(Barcelona_Data ) == "energy"] <- "energy_demand"

Removal of rows with errorenous data

Reference to average weather data for Barcelona

Barcelona_Data[Barcelona_Data$temp < 270 ,]
Barcelona_Data<- subset(Barcelona_Data, temp>270) # remove temperature less than 270K
Barcelona_Data<- subset(Barcelona_Data, pressure >900 & pressure <1050) # remove pressure outside [900,1050]mbar
Barcelona_Data<-subset(Barcelona_Data, energy_demand >10) # Remove all 0 demand from the data
Barcelona.rain_duration <- ggplot(Barcelona_Data) +
          geom_point( aes(c(1:length(rain_duration)),rain_duration)) +
          labs(subtitle="Rain Duration for Barcelona", 
          y="rain duration", 
          x="# data point") 

Barcelona.humidity <- ggplot(Barcelona_Data) +
          geom_point( aes(c(1:length(humidity)),humidity)) +
          labs(subtitle="Humidity for Barcelona", 
          y="humidity", 
          x="# data point")  

Barcelona.wind_speed <- ggplot(Barcelona_Data) +
          geom_point( aes(c(1:length(wind_speed)),wind_speed)) +
          labs(subtitle="Wind Speed for Barcelona", 
          y="wind_speed", 
          x="# data point")  

Barcelona.pressure <- ggplot(Barcelona_Data) +
          geom_point( aes(c(1:length(pressure)),pressure)) +
          labs(subtitle="Pressure of Barcelona", 
          y="pressure", 
          x="# data point")  

ggarrange(Barcelona.rain_duration,Barcelona.humidity,Barcelona.wind_speed,Barcelona.pressure,
          labels = c("(A)", "(B)", "(C)","(D)"),
          ncol = 2, nrow = 2)

FullModel <- lm(energy_demand ~ temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = Barcelona_Data)
summary(FullModel)$adj.r.squared
[1] 0.3125999

Adjusted R2 of the model is very low 0.3127159.

We perform Autocorrelation and Partial Autocorrelation to incorporate temporal variability/influence in the model.

Generation of AutoCorrelation and Partial Autocorrelation profile


# Opening the graphical device
# Customizing the output
pdf("acfpcf.pdf",         # File name
    width = 14, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model 
   

par(mfrow = c(1, 2))
Barcelona.acr <- acf(Barcelona_Data$energy_demand,plot = FALSE)
plot(Barcelona.acr, main = "Energy Demand of Barcelona", res=600, cex.main = 3)
grid(10,10)
Barcelona.pacr<- pacf(Barcelona_Data$energy_demand,plot =FALSE)
plot(Barcelona.pacr, main = "Energy Demand of Barcelona",res=600, cex.main = 3)
grid(10,10)

# Closing the graphical device
dev.off() 
null device 
          1 
pacf_values <-as.data.frame(Barcelona.pacr$acf)

pacf_values_detect<-subset(pacf_values, abs(pacf_values) >0.25) 

head(pacf_values_detect)
# Check the highest partially auto correlated energy demand in order
idx<-order(abs(pacf_values_detect$V1),decreasing = TRUE)
pacf_values_detect<- pacf_values_detect[order(abs(pacf_values_detect$V1),decreasing = TRUE),]

Create energy demand lag variable, one at a time

E_1<-Barcelona_Data$energy_demand[-1] # Get all the data except in first row 
Barcelona_Data<-Barcelona_Data[-nrow(Barcelona_Data),]
Barcelona_Data$E_1 <- E_1

E_2<-E_1[-1]
Barcelona_Data<-Barcelona_Data[-nrow(Barcelona_Data),]
Barcelona_Data$E_2 <- E_2

E_25<-Barcelona_Data$energy_demand[25:nrow(Barcelona_Data)] # Get all the data except in first row
Barcelona_Data<-Barcelona_Data[1:(nrow(Barcelona_Data)-24),]

Barcelona_Data$E_25 <- E_25

Check the model now

FullModel1 <- lm(energy_demand ~ E_1 + E_2 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = Barcelona_Data)
summary(FullModel1)

Call:
lm(formula = energy_demand ~ E_1 + E_2 + E_25 + temp + humidity + 
    pressure + wind_speed + rain_duration + factor(day_night) + 
    factor(time_band) + factor(season) + factor(weather_main), 
    data = Barcelona_Data)

Residuals:
     Min       1Q   Median       3Q      Max 
-1609.24   -50.99     8.82    62.86  2529.96 

Coefficients:
                          Estimate  Std. Error  t value             Pr(>|t|)    
(Intercept)            -443.913651   95.856207   -4.631      0.0000036514802 ***
E_1                       1.602696    0.004053  395.472 < 0.0000000000000002 ***
E_2                      -0.714152    0.003737 -191.080 < 0.0000000000000002 ***
E_25                      0.030901    0.001588   19.454 < 0.0000000000000002 ***
temp                      2.564276    0.162888   15.743 < 0.0000000000000002 ***
humidity                 -0.476128    0.041091  -11.587 < 0.0000000000000002 ***
pressure                  0.008830    0.085399    0.103             0.917648    
wind_speed                0.503034    0.320373    1.570             0.116389    
rain_duration             0.088170    1.063993    0.083             0.933957    
factor(day_night)2        6.985750    1.654274    4.223      0.0000241842532 ***
factor(time_band)2       26.676131    7.987014    3.340             0.000839 ***
factor(time_band)3       14.395365    6.215916    2.316             0.020570 *  
factor(season)2         -15.109435    2.308903   -6.544      0.0000000000607 ***
factor(season)3          -2.076330    1.806323   -1.149             0.250367    
factor(season)4          22.721483    1.974512   11.507 < 0.0000000000000002 ***
factor(weather_main)2    -0.735382    1.323482   -0.556             0.578459    
factor(weather_main)3    -2.926329    8.477791   -0.345             0.729964    
factor(weather_main)4   181.549670   79.215696    2.292             0.021921 *  
factor(weather_main)5     0.717756   14.634756    0.049             0.960884    
factor(weather_main)7   -15.212228    6.012154   -2.530             0.011403 *  
factor(weather_main)8     6.053918    2.536987    2.386             0.017026 *  
factor(weather_main)10  105.739970   30.017678    3.523             0.000428 ***
factor(weather_main)12    2.309079    7.752191    0.298             0.765811    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 112 on 34930 degrees of freedom
Multiple R-squared:  0.9591,    Adjusted R-squared:  0.9591 
F-statistic: 3.724e+04 on 22 and 34930 DF,  p-value: < 0.00000000000000022

Generate Working and Evaluation Data

Barcelona_Data <- Barcelona_Data [, c("time_ID", "E_1","E_2","E_25",   "temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy_demand")]


set.seed(501)
working_rows <- sample(1:nrow(Barcelona_Data),0.80*nrow(Barcelona_Data))

working_Barcelona <-Barcelona_Data[working_rows,]

working_Barcelona$index <- as.numeric(row.names(working_Barcelona))
working_Barcelona<- working_Barcelona[order(working_Barcelona$index), ]

working_Barcelona<-working_Barcelona %>%
                    relocate(index) 


evaluation_Barcelona <-Barcelona_Data[-working_rows,]
evaluation_Barcelona$index <- as.numeric(row.names(evaluation_Barcelona))
evaluation_Barcelona<- evaluation_Barcelona[order(evaluation_Barcelona$index), ]

evaluation_Barcelona<-evaluation_Barcelona %>%
                    relocate(index) 

write.csv(working_Barcelona,"Data/working_Barcelona.csv", row.names = FALSE)
write.csv(evaluation_Barcelona,"Data/evaluation_Barcelona.csv", row.names = FALSE)

Valencia DATA Preparation


Valencia_raw<-read.csv(file = "Data/Valencia.csv")
colnames(Valencia_raw)[colnames(Valencia_raw) == "X"] <- "DataID"
colnames(Valencia_raw)[colnames(Valencia_raw) == "dt_iso"] <- "time"
Valencia_Data<-Valencia_raw %>%
  dplyr::select(DataID,time,temp,humidity,pressure,wind_speed,rain_1h,rain_3h,snow_3h,weather_main,energy)

Valencia_Data<-transform(Valencia_Data, weather_main = factor(weather_main, 
                                                              levels = c("clear", "clouds", "drizzle","dust","fog","haze","mist","rain","smoke","snow","squall","thunderstorm"),
                                                              labels = c(1:12)))

# Add two columns of rain duration
Valencia_Data["rain_duration"] <- Valencia_Data$rain_1h + Valencia_Data$rain_3h 

# Rename Column Names for clarity 
Valencia_Data <-merge(Valencia_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)")], by.x = "time", by.y = "time")
colnames(Valencia_Data )[colnames(Valencia_Data ) == "Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)"] <- "time_band"

Valencia_Data <-merge(Valencia_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)")], by.x = "time", by.y = "time")
colnames(Valencia_Data )[colnames(Valencia_Data ) == "Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)"] <- "season"

Valencia_Data <-merge(Valencia_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime of day (Day vs Night)")], by.x = "time", by.y = "time")
colnames(Valencia_Data )[colnames(Valencia_Data ) == "Specified Categorical Variable:\r\nTime of day (Day vs Night)"] <- "day_night"


colnames(Valencia_Data )[colnames(Valencia_Data ) == "snow_3h"] <- "snow_duration"

Valencia_Data<-subset(Valencia_Data,select=-c(rain_1h,rain_3h,DataID))

colnames(Valencia_Data )[colnames(Valencia_Data ) == "time"] <- "time_ID"

Valencia_Data <- Valencia_Data [, c("time_ID","temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy")]

colnames(Valencia_Data )[colnames(Valencia_Data ) == "energy"] <- "energy_demand"

VD_<- Valencia_Data[Valencia_Data$temp < 270 ,]
Valencia_Data<- subset(Valencia_Data, temp>270) # remove temperature less than 270K
Valencia_Data<- subset(Valencia_Data, pressure >900 & pressure <1050) # remove pressure outside [900,1050]mbar
Valencia_Data<-subset(Valencia_Data, energy_demand >10) # Remove all 0 demand from the data

Valencia.rain_duration <- ggplot(Valencia_Data) +
  geom_point( aes(c(1:length(rain_duration)),rain_duration)) +
  labs(subtitle="Rain Duration for Valencia", 
       y="rain duration", 
       x="# data point") 

Valencia.humidity <- ggplot(Valencia_Data) +
  geom_point( aes(c(1:length(humidity)),humidity)) +
  labs(subtitle="Humidity for Valencia", 
       y="humidity", 
       x="# data point")  

Valencia.wind_speed <- ggplot(Valencia_Data) +
  geom_point( aes(c(1:length(wind_speed)),wind_speed)) +
  labs(subtitle="Wind Speed for Valencia", 
       y="wind_speed", 
       x="# data point")  

Valencia.pressure <- ggplot(Valencia_Data) +
  geom_point( aes(c(1:length(pressure)),pressure)) +
  labs(subtitle="Pressure of Valencia", 
       y="pressure", 
       x="# data point")  

ggarrange(Valencia.rain_duration,Valencia.humidity,Valencia.wind_speed,Valencia.pressure,
          labels = c("(A)", "(B)", "(C)","(D)"),
          ncol = 2, nrow = 2)



E_1<-Valencia_Data$energy_demand[-1] # Get all the data except in first row 
Valencia_Data<-Valencia_Data[-nrow(Valencia_Data),]
Valencia_Data$E_1 <- E_1

E_2<-E_1[-1]
Valencia_Data<-Valencia_Data[-nrow(Valencia_Data),]
Valencia_Data$E_2 <- E_2

E_25<-Valencia_Data$energy_demand[25:nrow(Valencia_Data)] # Get all the data except in first row
Valencia_Data<-Valencia_Data[1:(nrow(Valencia_Data)-24),]

Valencia_Data$E_25 <- E_25

Valencia_Data <- Valencia_Data [, c("time_ID", "E_1","E_2","E_25",   "temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy_demand")]


### Generate Working and Evaluation Data
set.seed(501)
working_rows <- sample(1:nrow(Valencia_Data),0.80*nrow(Valencia_Data))

working_Valencia <-Valencia_Data[working_rows,]

working_Valencia$index <- as.numeric(row.names(working_Valencia))
working_Valencia<- working_Valencia[order(working_Valencia$index), ]

working_Valencia<-working_Valencia %>%
  relocate(index) 


evaluation_Valencia <-Valencia_Data[-working_rows,]
evaluation_Valencia$index <- as.numeric(row.names(evaluation_Valencia))
evaluation_Valencia<- evaluation_Valencia[order(evaluation_Valencia$index), ]

evaluation_Valencia<-evaluation_Valencia %>%
  relocate(index) 

write.csv(working_Valencia,"Data/working_Valencia.csv", row.names = FALSE)
write.csv(evaluation_Valencia,"Data/evaluation_Valencia.csv", row.names = FALSE)

Bilbao DATA Preparation


Bilbao_raw<-read.csv(file = "Data/Bilbao.csv")
colnames(Bilbao_raw)[colnames(Bilbao_raw) == "X"] <- "DataID"
colnames(Bilbao_raw)[colnames(Bilbao_raw) == "dt_iso"] <- "time"
Bilbao_Data<-Bilbao_raw %>%
  dplyr::select(DataID,time,temp,humidity,pressure,wind_speed,rain_1h,rain_3h,snow_3h,weather_main,energy)

Bilbao_Data<-transform(Bilbao_Data, weather_main = factor(weather_main, 
                                                              levels = c("clear", "clouds", "drizzle","dust","fog","haze","mist","rain","smoke","snow","squall","thunderstorm"),
                                                              labels = c(1:12)))

# Add two columns of rain duration
Bilbao_Data["rain_duration"] <- Bilbao_Data$rain_1h + Bilbao_Data$rain_3h 

# Rename Column Names for clarity 
Bilbao_Data <-merge(Bilbao_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)")], by.x = "time", by.y = "time")
colnames(Bilbao_Data )[colnames(Bilbao_Data ) == "Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)"] <- "time_band"

Bilbao_Data <-merge(Bilbao_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)")], by.x = "time", by.y = "time")
colnames(Bilbao_Data )[colnames(Bilbao_Data ) == "Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)"] <- "season"

Bilbao_Data <-merge(Bilbao_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime of day (Day vs Night)")], by.x = "time", by.y = "time")
colnames(Bilbao_Data )[colnames(Bilbao_Data ) == "Specified Categorical Variable:\r\nTime of day (Day vs Night)"] <- "day_night"


colnames(Bilbao_Data )[colnames(Bilbao_Data ) == "snow_3h"] <- "snow_duration"

Bilbao_Data<-subset(Bilbao_Data,select=-c(rain_1h,rain_3h,DataID))

colnames(Bilbao_Data )[colnames(Bilbao_Data ) == "time"] <- "time_ID"

Bilbao_Data <- Bilbao_Data [, c("time_ID","temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy")]

colnames(Bilbao_Data )[colnames(Bilbao_Data ) == "energy"] <- "energy_demand"

BD_<- Bilbao_Data[Bilbao_Data$temp < 270 ,]
Bilbao_Data<- subset(Bilbao_Data, temp>270) # remove temperature less than 270K
Bilbao_Data<- subset(Bilbao_Data, pressure >900 & pressure <1050) # remove pressure outside [900,1050]mbar
Bilbao_Data<-subset(Bilbao_Data, energy_demand >10) # Remove all 0 demand from the data

Bilbao.rain_duration <- ggplot(Bilbao_Data) +
  geom_point( aes(c(1:length(rain_duration)),rain_duration)) +
  labs(subtitle="Rain Duration for Bilbao", 
       y="rain duration", 
       x="# data point") 

Bilbao.humidity <- ggplot(Bilbao_Data) +
  geom_point( aes(c(1:length(humidity)),humidity)) +
  labs(subtitle="Humidity for Bilbao", 
       y="humidity", 
       x="# data point")  

Bilbao.wind_speed <- ggplot(Bilbao_Data) +
  geom_point( aes(c(1:length(wind_speed)),wind_speed)) +
  labs(subtitle="Wind Speed for Bilbao", 
       y="wind_speed", 
       x="# data point")  

Bilbao.pressure <- ggplot(Bilbao_Data) +
  geom_point( aes(c(1:length(pressure)),pressure)) +
  labs(subtitle="Pressure of Bilbao", 
       y="pressure", 
       x="# data point")  

ggarrange(Bilbao.rain_duration,Bilbao.humidity,Bilbao.wind_speed,Bilbao.pressure,
          labels = c("(A)", "(B)", "(C)","(D)"),
          ncol = 2, nrow = 2)



E_1<-Bilbao_Data$energy_demand[-1] # Get all the data except in first row 
Bilbao_Data<-Bilbao_Data[-nrow(Bilbao_Data),]
Bilbao_Data$E_1 <- E_1

E_2<-E_1[-1]
Bilbao_Data<-Bilbao_Data[-nrow(Bilbao_Data),]
Bilbao_Data$E_2 <- E_2

E_25<-Bilbao_Data$energy_demand[25:nrow(Bilbao_Data)] # Get all the data except in first row
Bilbao_Data<-Bilbao_Data[1:(nrow(Bilbao_Data)-24),]

Bilbao_Data$E_25 <- E_25

Bilbao_Data <- Bilbao_Data [, c("time_ID", "E_1","E_2","E_25",   "temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy_demand")]


### Generate Working and Evaluation Data
set.seed(501)
working_rows <- sample(1:nrow(Bilbao_Data),0.80*nrow(Bilbao_Data))

working_Bilbao <-Bilbao_Data[working_rows,]

working_Bilbao$index <- as.numeric(row.names(working_Bilbao))
working_Bilbao<- working_Bilbao[order(working_Bilbao$index), ]

working_Bilbao<-working_Bilbao %>%
  relocate(index) 


evaluation_Bilbao <-Bilbao_Data[-working_rows,]
evaluation_Bilbao$index <- as.numeric(row.names(evaluation_Bilbao))
evaluation_Bilbao<- evaluation_Bilbao[order(evaluation_Bilbao$index), ]

evaluation_Bilbao<-evaluation_Bilbao %>%
  relocate(index) 

write.csv(working_Bilbao,"Data/working_Bilbao.csv", row.names = FALSE)
write.csv(evaluation_Bilbao,"Data/evaluation_Bilbao.csv", row.names = FALSE)

Seville DATA Preparation


Seville_raw<-read.csv(file = "Data/Seville.csv")
colnames(Seville_raw)[colnames(Seville_raw) == "X"] <- "DataID"
colnames(Seville_raw)[colnames(Seville_raw) == "dt_iso"] <- "time"
Seville_Data<-Seville_raw %>%
  dplyr::select(DataID,time,temp,humidity,pressure,wind_speed,rain_1h,rain_3h,snow_3h,weather_main,energy)

Seville_Data<-transform(Seville_Data, weather_main = factor(weather_main, 
                                                              levels = c("clear", "clouds", "drizzle","dust","fog","haze","mist","rain","smoke","snow","squall","thunderstorm"),
                                                              labels = c(1:12)))

# Add two columns of rain duration
Seville_Data["rain_duration"] <- Seville_Data$rain_1h + Seville_Data$rain_3h 

# Rename Column Names for clarity 
Seville_Data <-merge(Seville_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)")], by.x = "time", by.y = "time")
colnames(Seville_Data )[colnames(Seville_Data ) == "Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)"] <- "time_band"

Seville_Data <-merge(Seville_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)")], by.x = "time", by.y = "time")
colnames(Seville_Data )[colnames(Seville_Data ) == "Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)"] <- "season"

Seville_Data <-merge(Seville_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime of day (Day vs Night)")], by.x = "time", by.y = "time")
colnames(Seville_Data )[colnames(Seville_Data ) == "Specified Categorical Variable:\r\nTime of day (Day vs Night)"] <- "day_night"


colnames(Seville_Data )[colnames(Seville_Data ) == "snow_3h"] <- "snow_duration"

Seville_Data<-subset(Seville_Data,select=-c(rain_1h,rain_3h,DataID))

colnames(Seville_Data )[colnames(Seville_Data ) == "time"] <- "time_ID"

Seville_Data <- Seville_Data [, c("time_ID","temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy")]

colnames(Seville_Data )[colnames(Seville_Data ) == "energy"] <- "energy_demand"

SD_<-Seville_Data[Seville_Data$temp < 270 ,]
Seville_Data<- subset(Seville_Data, temp>270) # remove temperature less than 270K
Seville_Data<- subset(Seville_Data, pressure >900 & pressure <1050) # remove pressure outside [900,1050]mbar
Seville_Data<-subset(Seville_Data, energy_demand >10) # Remove all 0 demand from the data

Seville.rain_duration <- ggplot(Seville_Data) +
  geom_point( aes(c(1:length(rain_duration)),rain_duration)) +
  labs(subtitle="Rain Duration for Seville", 
       y="rain duration", 
       x="# data point") 

Seville.humidity <- ggplot(Seville_Data) +
  geom_point( aes(c(1:length(humidity)),humidity)) +
  labs(subtitle="Humidity for Seville", 
       y="humidity", 
       x="# data point")  

Seville.wind_speed <- ggplot(Seville_Data) +
  geom_point( aes(c(1:length(wind_speed)),wind_speed)) +
  labs(subtitle="Wind Speed for Seville", 
       y="wind_speed", 
       x="# data point")  

Seville.pressure <- ggplot(Seville_Data) +
  geom_point( aes(c(1:length(pressure)),pressure)) +
  labs(subtitle="Pressure of Seville", 
       y="pressure", 
       x="# data point")  

ggarrange(Seville.rain_duration,Seville.humidity,Seville.wind_speed,Seville.pressure,
          labels = c("(A)", "(B)", "(C)","(D)"),
          ncol = 2, nrow = 2)



E_1<-Seville_Data$energy_demand[-1] # Get all the data except in first row 
Seville_Data<-Seville_Data[-nrow(Seville_Data),]
Seville_Data$E_1 <- E_1

E_2<-E_1[-1]
Seville_Data<-Seville_Data[-nrow(Seville_Data),]
Seville_Data$E_2 <- E_2

E_25<-Seville_Data$energy_demand[25:nrow(Seville_Data)] # Get all the data except in first row
Seville_Data<-Seville_Data[1:(nrow(Seville_Data)-24),]

Seville_Data$E_25 <- E_25

Seville_Data <- Seville_Data [, c("time_ID", "E_1","E_2","E_25",   "temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy_demand")]


### Generate Working and Evaluation Data
set.seed(501)
working_rows <- sample(1:nrow(Seville_Data),0.80*nrow(Seville_Data))

working_Seville <-Seville_Data[working_rows,]

working_Seville$index <- as.numeric(row.names(working_Seville))
working_Seville<- working_Seville[order(working_Seville$index), ]

working_Seville<-working_Seville %>%
  relocate(index) 


evaluation_Seville <-Seville_Data[-working_rows,]
evaluation_Seville$index <- as.numeric(row.names(evaluation_Seville))
evaluation_Seville<- evaluation_Seville[order(evaluation_Seville$index), ]

evaluation_Seville<-evaluation_Seville %>%
  relocate(index) 

write.csv(working_Seville,"Data/working_Seville.csv", row.names = FALSE)
write.csv(evaluation_Seville,"Data/evaluation_Seville.csv", row.names = FALSE)

Madrid DATA Preparation


Madrid_raw<-read.csv(file = "Data/Madrid.csv")
colnames(Madrid_raw)[colnames(Madrid_raw) == "X"] <- "DataID"
colnames(Madrid_raw)[colnames(Madrid_raw) == "dt_iso"] <- "time"
Madrid_Data<-Madrid_raw %>%
  dplyr::select(DataID,time,temp,humidity,pressure,wind_speed,rain_1h,rain_3h,snow_3h,weather_main,energy)

Madrid_Data<-transform(Madrid_Data, weather_main = factor(weather_main, 
                                                              levels = c("clear", "clouds", "drizzle","dust","fog","haze","mist","rain","smoke","snow","squall","thunderstorm"),
                                                              labels = c(1:12)))

# Add two columns of rain duration
Madrid_Data["rain_duration"] <- Madrid_Data$rain_1h + Madrid_Data$rain_3h 

# Rename Column Names for clarity 
Madrid_Data <-merge(Madrid_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)")], by.x = "time", by.y = "time")
colnames(Madrid_Data )[colnames(Madrid_Data ) == "Specified Categorical Variable:\r\nTime Band (Peak, Off-Peak, Mid-Peak)"] <- "time_band"

Madrid_Data <-merge(Madrid_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)")], by.x = "time", by.y = "time")
colnames(Madrid_Data )[colnames(Madrid_Data ) == "Specified Categorical Variable:\r\nSeason (Spring, Summer, Autumn, Winter)"] <- "season"

Madrid_Data <-merge(Madrid_Data, energy_raw[,c("time","Specified Categorical Variable:\r\nTime of day (Day vs Night)")], by.x = "time", by.y = "time")
colnames(Madrid_Data )[colnames(Madrid_Data ) == "Specified Categorical Variable:\r\nTime of day (Day vs Night)"] <- "day_night"


colnames(Madrid_Data )[colnames(Madrid_Data ) == "snow_3h"] <- "snow_duration"

Madrid_Data<-subset(Madrid_Data,select=-c(rain_1h,rain_3h,DataID))

colnames(Madrid_Data )[colnames(Madrid_Data ) == "time"] <- "time_ID"

Madrid_Data <- Madrid_Data [, c("time_ID","temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy")]

colnames(Madrid_Data )[colnames(Madrid_Data ) == "energy"] <- "energy_demand"

MD_ <- Madrid_Data[Madrid_Data$temp < 270 ,]
Madrid_Data<- subset(Madrid_Data, temp>270) # remove temperature less than 270K
Madrid_Data<- subset(Madrid_Data, pressure >900 & pressure <1050) # remove pressure outside [900,1050]mbar
Madrid_Data<-subset(Madrid_Data, energy_demand >10) # Remove all 0 demand from the data

Madrid.rain_duration <- ggplot(Madrid_Data) +
  geom_point( aes(c(1:length(rain_duration)),rain_duration)) +
  labs(subtitle="Rain Duration for Madrid", 
       y="rain duration", 
       x="# data point") 

Madrid.humidity <- ggplot(Madrid_Data) +
  geom_point( aes(c(1:length(humidity)),humidity)) +
  labs(subtitle="Humidity for Madrid", 
       y="humidity", 
       x="# data point")  

Madrid.wind_speed <- ggplot(Madrid_Data) +
  geom_point( aes(c(1:length(wind_speed)),wind_speed)) +
  labs(subtitle="Wind Speed for Madrid", 
       y="wind_speed", 
       x="# data point")  

Madrid.pressure <- ggplot(Madrid_Data) +
  geom_point( aes(c(1:length(pressure)),pressure)) +
  labs(subtitle="Pressure of Madrid", 
       y="pressure", 
       x="# data point")  

ggarrange(Madrid.rain_duration,Madrid.humidity,Madrid.wind_speed,Madrid.pressure,
          labels = c("(A)", "(B)", "(C)","(D)"),
          ncol = 2, nrow = 2)



E_1<-Madrid_Data$energy_demand[-1] # Get all the data except in first row 
Madrid_Data<-Madrid_Data[-nrow(Madrid_Data),]
Madrid_Data$E_1 <- E_1

E_2<-E_1[-1]
Madrid_Data<-Madrid_Data[-nrow(Madrid_Data),]
Madrid_Data$E_2 <- E_2

E_25<-Madrid_Data$energy_demand[25:nrow(Madrid_Data)] # Get all the data except in first row
Madrid_Data<-Madrid_Data[1:(nrow(Madrid_Data)-24),]

Madrid_Data$E_25 <- E_25

Madrid_Data <- Madrid_Data [, c("time_ID", "E_1","E_2","E_25",   "temp","humidity","pressure","wind_speed","rain_duration","snow_duration","day_night","time_band","season","weather_main","energy_demand")]


### Generate Working and Evaluation Data
set.seed(501)
working_rows <- sample(1:nrow(Madrid_Data),0.80*nrow(Madrid_Data))

working_Madrid <-Madrid_Data[working_rows,]

working_Madrid$index <- as.numeric(row.names(working_Madrid))
working_Madrid<- working_Madrid[order(working_Madrid$index), ]

working_Madrid<-working_Madrid %>%
  relocate(index) 


evaluation_Madrid <-Madrid_Data[-working_rows,]
evaluation_Madrid$index <- as.numeric(row.names(evaluation_Madrid))
evaluation_Madrid<- evaluation_Madrid[order(evaluation_Madrid$index), ]

evaluation_Madrid<-evaluation_Madrid %>%
  relocate(index) 

write.csv(working_Madrid,"Data/working_Madrid.csv", row.names = FALSE)
write.csv(evaluation_Madrid,"Data/evaluation_Madrid.csv", row.names = FALSE)

MLR Modelling

Barcelona Model

Data Processing

Read working and evaluation data set, moreover, remove the index and time stamp and snow_duration variable as these are all invalid variables (the latter as all entries are zero)

evaldata<-read.csv('data/evaluation_Barcelona.csv')
workdata<-read.csv('data/working_Barcelona.csv')
#remove index, time_ID and snow_duration columns from "eval" analysis data frame
evalBarcelona<-evaldata[c(-1,-2,-11)] 
#remove index, time_ID and snow_duration columns from "work" analysis data frame
workBarcelona<-workdata[c(-1,-2,-11)] 
# head(workBarcelona, 4L)

Create the ggpair plot and Correlation plot

# Correlation matrix for numeric predictors only (categorical predictors excluded)
workcor<-workBarcelona[c(-9:-12) ]
ggpairs(workcor)

cor(workBarcelona)
                      E_1         E_2        E_25        temp    humidity    pressure  wind_speed rain_duration    day_night    time_band      season
E_1            1.00000000  0.95043237  0.66580794  0.13878927 -0.28177561 -0.02781520  0.12785107   -0.01756328 -0.546196259  0.111967183  0.08088787
E_2            0.95043237  1.00000000  0.56952817  0.10748859 -0.24873502 -0.03648859  0.12975971   -0.01762592 -0.539309783  0.091879443  0.07961938
E_25           0.66580794  0.56952817  1.00000000  0.15876073 -0.31888324 -0.03315805  0.14094675   -0.01457261 -0.508755131  0.112525926  0.08003023
temp           0.13878927  0.10748859  0.15876073  1.00000000 -0.29083714 -0.08811130  0.08937600    0.03851107 -0.319483326 -0.103859527 -0.34479411
humidity      -0.28177561 -0.24873502 -0.31888324 -0.29083714  1.00000000  0.10761304 -0.22672868    0.04008084  0.369843046 -0.097717527  0.04568793
pressure      -0.02781520 -0.03648859 -0.03315805 -0.08811130  0.10761304  1.00000000 -0.12403082   -0.08243537  0.042578653 -0.030693135  0.22132029
wind_speed     0.12785107  0.12975971  0.14094675  0.08937600 -0.22672868 -0.12403082  1.00000000    0.03733744 -0.237455373 -0.000365960 -0.02069053
rain_duration -0.01756328 -0.01762592 -0.01457261  0.03851107  0.04008084 -0.08243537  0.03733744    1.00000000 -0.013813870  0.024856915 -0.03184723
day_night     -0.54619626 -0.53930978 -0.50875513 -0.31948333  0.36984305  0.04257865 -0.23745537   -0.01381387  1.000000000  0.001813599  0.15078914
time_band      0.11196718  0.09187944  0.11252593 -0.10385953 -0.09771753 -0.03069313 -0.00036596    0.02485691  0.001813599  1.000000000  0.16363562
season         0.08088787  0.07961938  0.08003023 -0.34479411  0.04568793  0.22132029 -0.02069053   -0.03184723  0.150789145  0.163635620  1.00000000
weather_main  -0.01894161 -0.01580890 -0.01977277 -0.03246087  0.18608532 -0.18441348  0.05154379    0.43511910  0.007496362 -0.012094920 -0.01586947
energy_demand  0.95024655  0.83107411  0.69947826  0.16573813 -0.30543266 -0.01938825  0.12361053   -0.01523665 -0.512051575  0.123752931  0.08143799
              weather_main energy_demand
E_1           -0.018941608    0.95024655
E_2           -0.015808902    0.83107411
E_25          -0.019772771    0.69947826
temp          -0.032460871    0.16573813
humidity       0.186085318   -0.30543266
pressure      -0.184413485   -0.01938825
wind_speed     0.051543786    0.12361053
rain_duration  0.435119102   -0.01523665
day_night      0.007496362   -0.51205158
time_band     -0.012094920    0.12375293
season        -0.015869470    0.08143799
weather_main   1.000000000   -0.02147315
energy_demand -0.021473153    1.00000000

Create color spectrum correlation plot

pdf("corplot.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
corrplot(cor(workcor), method="color", type="full", addCoef.col = "red", tl.col="black",number.cex = 0.75)
dev.off()
null device 
          1 
corrplot(cor(workcor), method="color", type="full", addCoef.col = "red", tl.col="black",number.cex = 0.75)

Model Processing

Build the full model

workModel1 <- lm(energy_demand ~ E_1 + E_2 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBarcelona)

Check the multicollinearity assumption

vif(workModel1)
                          GVIF Df GVIF^(1/(2*Df))
E_1                  13.942127  1        3.733916
E_2                  11.846657  1        3.441897
E_25                  2.148160  1        1.465660
temp                  3.375736  1        1.837318
humidity              1.444593  1        1.201912
pressure              1.163925  1        1.078854
wind_speed            1.140701  1        1.068036
rain_duration         1.350867  1        1.162268
factor(day_night)     1.913604  1        1.383331
factor(time_band)     1.102038  2        1.024588
factor(season)        3.546552  3        1.234907
factor(weather_main)  1.581208  8        1.029051
multicol_<-vif(workModel1)
print("VIF values greater than 10, implying serious multicollinearity, are:")
[1] "VIF values greater than 10, implying serious multicollinearity, are:"
multicol_[multicol_[,"GVIF"]>10,1]
     E_1      E_2 
13.94213 11.84666 

According to the VIF output E_1 and E_2 variables have serious multicollinearity issue as thus remove E_2 and rebuild the model.

Drop E_2 to address multicollinearity between E_1 and E_2, and rebuild the model

workModelFull <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBarcelona)
vif(workModelFull)
                         GVIF Df GVIF^(1/(2*Df))
E_1                  2.049752  1        1.431696
E_25                 1.962199  1        1.400785
temp                 3.305833  1        1.818195
humidity             1.444100  1        1.201707
pressure             1.161911  1        1.077920
wind_speed           1.139121  1        1.067296
rain_duration        1.350681  1        1.162188
factor(day_night)    1.849385  1        1.359921
factor(time_band)    1.098891  2        1.023855
factor(season)       3.487460  3        1.231454
factor(weather_main) 1.580054  8        1.029004

According to the VIF output, we can observe that there’s no serious multicollinearity issue.

Model assumption check

# to obtain residuals
res.fullmodel1 <- residuals(workModelFull) 
# to obtain standardized residuals
std.res.fullmodel1 <- rstandard(workModelFull) 
# to obtain fitted/predicted values
pred.fullmodel1 <- fitted.values(workModelFull) 
par(mfrow=c(1,1))
qqnorm(y = std.res.fullmodel1, main = " Normal Q-Q Plot ",
       xlab = "Theoretical Quantiles", ylab = "Sample Quantiles")
qqline(y = std.res.fullmodel1)

resplotdata1 <- data.frame(std.res.fullmodel1, pred.fullmodel1)
resbf1 <- lm(std.res.fullmodel1 ~ pred.fullmodel1, data = resplotdata1)
plot(x = pred.fullmodel1, y = std.res.fullmodel1, ylab = "Standardized Residuals", xlab = "Predicted Values", main = "Residuals Plot", col = ifelse(std.res.fullmodel1 < -3,"red",ifelse(std.res.fullmodel1 > 3,"red","black")))
abline(h = 0, col="blue", lty=1)
abline(resbf1, col="red", lty=3)
abline(h = 3, col="green", lty=3)
abline(h=-3, col="green", lty=3)
legend("bottomleft", legend=c("Best fit line of standardized residuals", "Horizontal line y = 0.0", "Horizontal line, y = +/- 3"), fill = c("red","blue","green"), cex = 1.0)

According to the Normal QQ plot, we can observe that most of points align on the reference line thus it follows the normal distribution assumption.

According to the residual plot, we can observe that there’s no heteroscedasticity issue , nevertheless, there are numerous outliers.

Check the regression output and build the reduce model

Based on regression summary, predictor rain_duration dropped as insignificant (p-values is > 0.05). Build a reduce model then compare with the full model

summary(workModelFull)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    wind_speed + rain_duration + factor(day_night) + factor(time_band) + 
    factor(season) + factor(weather_main), data = workBarcelona)

Residuals:
     Min       1Q   Median       3Q      Max 
-1531.74   -90.57    14.60    99.89  1412.55 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -2803.519570   152.435690 -18.391 < 0.0000000000000002 ***
E_1                        0.885257     0.002489 355.738 < 0.0000000000000002 ***
E_25                       0.121752     0.002432  50.067 < 0.0000000000000002 ***
temp                       7.170224     0.260203  27.556 < 0.0000000000000002 ***
humidity                  -0.634227     0.065866  -9.629 < 0.0000000000000002 ***
pressure                   0.730545     0.136673   5.345       0.000000091017 ***
wind_speed                -1.977760     0.514097  -3.847              0.00012 ***
rain_duration              2.422471     1.711138   1.416              0.15687    
factor(day_night)2        65.503160     2.613422  25.064 < 0.0000000000000002 ***
factor(time_band)2       -18.244439    12.868697  -1.418              0.15628    
factor(time_band)3        62.331534    10.004587   6.230       0.000000000472 ***
factor(season)2          -73.426137     3.686941 -19.915 < 0.0000000000000002 ***
factor(season)3          -29.983347     2.900109 -10.339 < 0.0000000000000002 ***
factor(season)4           17.277737     3.165117   5.459       0.000000048344 ***
factor(weather_main)2     -2.322696     2.122710  -1.094              0.27387    
factor(weather_main)3    -13.355521    13.517978  -0.988              0.32317    
factor(weather_main)4    156.628292   113.610051   1.379              0.16801    
factor(weather_main)5     16.796013    22.805446   0.736              0.46144    
factor(weather_main)7    -43.788142     9.722793  -4.504       0.000006706350 ***
factor(weather_main)8     13.107792     4.065549   3.224              0.00127 ** 
factor(weather_main)10   117.912863    48.565247   2.428              0.01519 *  
factor(weather_main)12     3.124515    12.573762   0.248              0.80375    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 160.6 on 27940 degrees of freedom
Multiple R-squared:  0.9156,    Adjusted R-squared:  0.9156 
F-statistic: 1.444e+04 on 21 and 27940 DF,  p-value: < 0.00000000000000022
df<- workModelFull %>% 
  tidy()
#Get the variables which there p-value larger than 0.05 
df%>%
  filter(df$p.value>0.05)
workModelPvalue <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBarcelona)
summary(workModelPvalue)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    wind_speed + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main), data = workBarcelona)

Residuals:
     Min       1Q   Median       3Q      Max 
-1532.03   -90.58    14.64    99.91  1412.13 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -2804.180567   152.437714 -18.396 < 0.0000000000000002 ***
E_1                        0.885189     0.002488 355.770 < 0.0000000000000002 ***
E_25                       0.121702     0.002432  50.051 < 0.0000000000000002 ***
temp                       7.162242     0.260147  27.532 < 0.0000000000000002 ***
humidity                  -0.638104     0.065810  -9.696 < 0.0000000000000002 ***
pressure                   0.734014     0.136653   5.371       0.000000078767 ***
wind_speed                -1.962770     0.513997  -3.819             0.000134 ***
factor(day_night)2        65.425220     2.612889  25.039 < 0.0000000000000002 ***
factor(time_band)2       -17.154791    12.845890  -1.335             0.181746    
factor(time_band)3        62.623435    10.002642   6.261       0.000000000389 ***
factor(season)2          -72.999238     3.674655 -19.866 < 0.0000000000000002 ***
factor(season)3          -29.911221     2.899713 -10.315 < 0.0000000000000002 ***
factor(season)4           17.237276     3.165045   5.446       0.000000051908 ***
factor(weather_main)2     -2.311979     2.122735  -1.089             0.276097    
factor(weather_main)3    -13.139596    13.517361  -0.972             0.331032    
factor(weather_main)4    156.902134   113.611928   1.381             0.167279    
factor(weather_main)5     16.929897    22.805659   0.742             0.457878    
factor(weather_main)7    -43.540310     9.721391  -4.479       0.000007535584 ***
factor(weather_main)8     15.811254     3.589221   4.405       0.000010607663 ***
factor(weather_main)10   118.504216    48.564323   2.440             0.014687 *  
factor(weather_main)12     3.569918    12.570052   0.284             0.776411    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 160.6 on 27941 degrees of freedom
Multiple R-squared:  0.9156,    Adjusted R-squared:  0.9156 
F-statistic: 1.516e+04 on 20 and 27941 DF,  p-value: < 0.00000000000000022

Reduced model comparisons

  • p-value approach
anova(workModelPvalue, workModelFull)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1  27941 720280919                           
2  27940 720229254  1     51664 2.0042 0.1569

At 5% significance level , no statistical evidence to support that the full model is preferred over than reduced model.(it’s simpler… no prediction performance is lost via the elimination of identified predictors). That is to say, workModelPvalue is preferred over workModelFull.

  • Best subset selection method

Based on the the Best subset selection method, we drop pressue, wind_speed, rain_duration, time_band and weather_main to build the reduce model.

bestfits <- regsubsets(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBarcelona, nbest = 1)
plot(bestfits, scale="adjr2")

workModelBestfit<-lm(energy_demand ~ E_1 + E_25 + temp + humidity + factor(day_night) + factor(season) , data = workBarcelona)
summary(workModelBestfit)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + factor(day_night) + 
    factor(season), data = workBarcelona)

Residuals:
     Min       1Q   Median       3Q      Max 
-1543.34   -90.26    15.10    99.63  1400.13 

Coefficients:
                       Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)        -2052.560979    76.260658 -26.915 < 0.0000000000000002 ***
E_1                    0.885823     0.002487 356.180 < 0.0000000000000002 ***
E_25                   0.122668     0.002431  50.459 < 0.0000000000000002 ***
temp                   7.080417     0.257109  27.539 < 0.0000000000000002 ***
humidity              -0.566084     0.063212  -8.955 < 0.0000000000000002 ***
factor(day_night)2    67.594434     2.564110  26.362 < 0.0000000000000002 ***
factor(season)2      -70.964302     3.649244 -19.446 < 0.0000000000000002 ***
factor(season)3      -27.809163     2.884814  -9.640 < 0.0000000000000002 ***
factor(season)4       21.157266     3.034653   6.972      0.0000000000032 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 160.9 on 27953 degrees of freedom
Multiple R-squared:  0.9152,    Adjusted R-squared:  0.9152 
F-statistic: 3.77e+04 on 8 and 27953 DF,  p-value: < 0.00000000000000022
anova(workModelBestfit, workModelPvalue)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + factor(day_night) + 
    factor(season)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F                Pr(>F)    
1  27953 723907059                                              
2  27941 720280919 12   3626141 11.722 < 0.00000000000000022 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

At 5% significance level , no statistical evidence to support that the Bestfit model is preferred over than Pvalue model.(it’s simpler… no prediction performance is lost via the elimination of identified predictors). That is to say, workModelPvalue is preferred over workModelBestfit.

  • AIC with forward selection approach test method
workNullModel <- lm(energy_demand ~ 1, data = workBarcelona)
step.working <- stepAIC(workNullModel, scope = list(lower = workNullModel,
upper = workModelFull), direction = "forward", trace=FALSE)
summary(step.working)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + factor(day_night) + 
    temp + factor(season) + humidity + factor(time_band) + factor(weather_main) + 
    pressure + wind_speed + rain_duration, data = workBarcelona)

Residuals:
     Min       1Q   Median       3Q      Max 
-1531.74   -90.57    14.60    99.89  1412.55 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -2803.519570   152.435690 -18.391 < 0.0000000000000002 ***
E_1                        0.885257     0.002489 355.738 < 0.0000000000000002 ***
E_25                       0.121752     0.002432  50.067 < 0.0000000000000002 ***
factor(day_night)2        65.503160     2.613422  25.064 < 0.0000000000000002 ***
temp                       7.170224     0.260203  27.556 < 0.0000000000000002 ***
factor(season)2          -73.426137     3.686941 -19.915 < 0.0000000000000002 ***
factor(season)3          -29.983347     2.900109 -10.339 < 0.0000000000000002 ***
factor(season)4           17.277737     3.165117   5.459       0.000000048344 ***
humidity                  -0.634227     0.065866  -9.629 < 0.0000000000000002 ***
factor(time_band)2       -18.244439    12.868697  -1.418              0.15628    
factor(time_band)3        62.331534    10.004587   6.230       0.000000000472 ***
factor(weather_main)2     -2.322696     2.122710  -1.094              0.27387    
factor(weather_main)3    -13.355521    13.517978  -0.988              0.32317    
factor(weather_main)4    156.628292   113.610051   1.379              0.16801    
factor(weather_main)5     16.796013    22.805446   0.736              0.46144    
factor(weather_main)7    -43.788142     9.722793  -4.504       0.000006706350 ***
factor(weather_main)8     13.107792     4.065549   3.224              0.00127 ** 
factor(weather_main)10   117.912863    48.565247   2.428              0.01519 *  
factor(weather_main)12     3.124515    12.573762   0.248              0.80375    
pressure                   0.730545     0.136673   5.345       0.000000091017 ***
wind_speed                -1.977760     0.514097  -3.847              0.00012 ***
rain_duration              2.422471     1.711138   1.416              0.15687    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 160.6 on 27940 degrees of freedom
Multiple R-squared:  0.9156,    Adjusted R-squared:  0.9156 
F-statistic: 1.444e+04 on 21 and 27940 DF,  p-value: < 0.00000000000000022

According to the AIC method output, we get the same multiple linear model as full model.

  • Cross Validation test method
# Set seed for reproducibility in CV
set.seed(123)
# Set up repeated k-fold cross-validation, with k=10
train.control <- trainControl(method = "cv", number = 10)
# Train the model
step.cv.work <- train(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBarcelona, method = "leapBackward",
tuneGrid = data.frame(nvmax = 1:11),
trControl = train.control)
step.cv.work$results
summary(step.cv.work$finalModel)
Subset selection object
21 Variables  (and intercept)
                       Forced in Forced out
E_1                        FALSE      FALSE
E_25                       FALSE      FALSE
temp                       FALSE      FALSE
humidity                   FALSE      FALSE
pressure                   FALSE      FALSE
wind_speed                 FALSE      FALSE
rain_duration              FALSE      FALSE
factor(day_night)2         FALSE      FALSE
factor(time_band)2         FALSE      FALSE
factor(time_band)3         FALSE      FALSE
factor(season)2            FALSE      FALSE
factor(season)3            FALSE      FALSE
factor(season)4            FALSE      FALSE
factor(weather_main)2      FALSE      FALSE
factor(weather_main)3      FALSE      FALSE
factor(weather_main)4      FALSE      FALSE
factor(weather_main)5      FALSE      FALSE
factor(weather_main)7      FALSE      FALSE
factor(weather_main)8      FALSE      FALSE
factor(weather_main)10     FALSE      FALSE
factor(weather_main)12     FALSE      FALSE
1 subsets of each size up to 11
Selection Algorithm: backward
          E_1 E_25 temp humidity pressure wind_speed rain_duration factor(day_night)2 factor(time_band)2 factor(time_band)3 factor(season)2
1  ( 1 )  "*" " "  " "  " "      " "      " "        " "           " "                " "                " "                " "            
2  ( 1 )  "*" "*"  " "  " "      " "      " "        " "           " "                " "                " "                " "            
3  ( 1 )  "*" "*"  " "  " "      " "      " "        " "           "*"                " "                " "                " "            
4  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           "*"                " "                " "                " "            
5  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           "*"                " "                " "                "*"            
6  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           "*"                " "                " "                "*"            
7  ( 1 )  "*" "*"  "*"  "*"      " "      " "        " "           "*"                " "                " "                "*"            
8  ( 1 )  "*" "*"  "*"  "*"      " "      " "        " "           "*"                " "                "*"                "*"            
9  ( 1 )  "*" "*"  "*"  "*"      "*"      " "        " "           "*"                " "                "*"                "*"            
10  ( 1 ) "*" "*"  "*"  "*"      "*"      " "        " "           "*"                " "                "*"                "*"            
11  ( 1 ) "*" "*"  "*"  "*"      "*"      " "        " "           "*"                " "                "*"                "*"            
          factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)4 factor(weather_main)5 factor(weather_main)7
1  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
2  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
3  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
4  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
5  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
6  ( 1 )  "*"             " "             " "                   " "                   " "                   " "                   " "                  
7  ( 1 )  "*"             " "             " "                   " "                   " "                   " "                   " "                  
8  ( 1 )  "*"             " "             " "                   " "                   " "                   " "                   " "                  
9  ( 1 )  "*"             " "             " "                   " "                   " "                   " "                   " "                  
10  ( 1 ) "*"             " "             " "                   " "                   " "                   " "                   " "                  
11  ( 1 ) "*"             "*"             " "                   " "                   " "                   " "                   " "                  
          factor(weather_main)8 factor(weather_main)10 factor(weather_main)12
1  ( 1 )  " "                   " "                    " "                   
2  ( 1 )  " "                   " "                    " "                   
3  ( 1 )  " "                   " "                    " "                   
4  ( 1 )  " "                   " "                    " "                   
5  ( 1 )  " "                   " "                    " "                   
6  ( 1 )  " "                   " "                    " "                   
7  ( 1 )  " "                   " "                    " "                   
8  ( 1 )  " "                   " "                    " "                   
9  ( 1 )  " "                   " "                    " "                   
10  ( 1 ) "*"                   " "                    " "                   
11  ( 1 ) "*"                   " "                    " "                   
workModelCross <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBarcelona)
anova( workModelCross, workModelPvalue)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + factor(day_night) + 
    factor(time_band) + factor(season) + factor(weather_main)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F    Pr(>F)    
1  27942 720656824                                  
2  27941 720280919  1    375905 14.582 0.0001345 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
summary(workModelCross)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main), data = workBarcelona)

Residuals:
     Min       1Q   Median       3Q      Max 
-1539.30   -90.57    14.51    99.82  1403.40 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -2842.315313   152.147211 -18.681 < 0.0000000000000002 ***
E_1                        0.885397     0.002488 355.852 < 0.0000000000000002 ***
E_25                       0.121666     0.002432  50.024 < 0.0000000000000002 ***
temp                       7.085295     0.259428  27.311 < 0.0000000000000002 ***
humidity                  -0.607712     0.065343  -9.300 < 0.0000000000000002 ***
pressure                   0.784867     0.136036   5.770       0.000000008032 ***
factor(day_night)2        66.722012     2.591356  25.748 < 0.0000000000000002 ***
factor(time_band)2       -15.310566    12.839927  -1.192               0.2331    
factor(time_band)3        63.568928    10.002007   6.356       0.000000000211 ***
factor(season)2          -71.764888     3.661300 -19.601 < 0.0000000000000002 ***
factor(season)3          -29.174014     2.893983 -10.081 < 0.0000000000000002 ***
factor(season)4           16.333074     3.156942   5.174       0.000000231086 ***
factor(weather_main)2     -3.183514     2.110943  -1.508               0.1315    
factor(weather_main)3    -13.335646    13.520548  -0.986               0.3240    
factor(weather_main)4    151.166860   113.629607   1.330               0.1834    
factor(weather_main)5     15.960942    22.809789   0.700               0.4841    
factor(weather_main)7    -43.087845     9.723032  -4.432       0.000009392517 ***
factor(weather_main)8     14.635296     3.576855   4.092       0.000042948541 ***
factor(weather_main)10   120.377548    48.573646   2.478               0.0132 *  
factor(weather_main)12     2.616717    12.570627   0.208               0.8351    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 160.6 on 27942 degrees of freedom
Multiple R-squared:  0.9156,    Adjusted R-squared:  0.9155 
F-statistic: 1.595e+04 on 19 and 27942 DF,  p-value: < 0.00000000000000022

At 5% significance level , no statistical evidence to support that the Cross Validation model is preferred over than Pvalue model. (Latter is simpler… no prediction performance is lost via the elimination of identified predictors). That is to say, workModelPvalue is preferred over workModelCross

  • OLS method
#ModelOLS <- ols_step_all_possible(workModelFull)
#cat("\n")
#plot(x = ModelOLS$n, y = ModelOLS$adjr)
#plot(x = ModelOLS$n, y = ModelOLS$aic)
#OLSsummary <- ModelOLS %>% group_by(n) %>%  filter(adjr == max(adjr))
#OLSsummary

Based on Adjusted R-square, the most favorable OLS model is equivalent to workModelFull.

Select the final model

Method <- c('Full', 'P-value', 'BestFits', 'AIC Forward', 'Cross Validation','OLS_step')
Variables <- c(12,11,7,12,10,12)
Coefficients <- c(22,21,9,22,20,21)
Adjusted_R2 <- c(0.9155554,0.9155523,0.9151636,0.9155554,0.9155113,0.9155523)
BarcelonaTable<-data.frame(Method,Variables,Coefficients,Adjusted_R2)
# Summary table for different selection methods
print(BarcelonaTable)
#Alternate order
#ARSlist <- list(FullARS = c(summary(workModelFull)$adj.r.squared),
#                PvalueARS = c(summary(workModelPvalue)$adj.r.squared),
#                BestfitsARS = c(summary(workModelBestfit)$adj.r.squared),
#                AICARS = c(summary(step.working)$adj.r.squared),
#                CrossvARS = c(summary(workModelCross)$adj.r.squared),
#                OLSARS = max(OLSsummary$adjr))
#ModelOrder <- as.data.frame(ARSlist) %>% pivot_longer(cols = 1:4) %>% arrange(desc(value))
#ModelOrder

Model Adjusted R-square from above methods (to 4 significant figures)

  1. Full: 0.9156

  2. P-value: 0.9156 (viewed to be the same as Full)

  3. Bestfits: 0.9152

  4. AIC Method: 0.9156

  5. Cross-validation: 0.9155

  6. OLS-step: 0.9156 (viewed to be the same as P-value)

Final model is the workModelPvalue as it has the highest Adjusted R-square and fewest predictors.

Build the Final Model

workFinal <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBarcelona)

Check the linearity, heteroscedasticity, outlier and normal assumption

par(mfrow=c(2,2))
#Residual Plot
p1<-plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
#norm test
std.res <- rstandard(workFinal)
ad.test(std.res)

    Anderson-Darling normality test

data:  std.res
A = 122.77, p-value < 0.00000000000000022
p2<-qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
#leverage 
lev<-hatvalues(workFinal)
cutlev = (2*length(coefficients(workFinal)))/nrow(workBarcelona)
# Count and assess the number of leverage values > cut-off
potoutlier <- sum(lev > cutlev, na.rm=TRUE)
totcount = length(fitted(workFinal))
print(paste("The number of leverage points that are potential outliers is", potoutlier,".  This is", round(100*potoutlier/totcount,2),"% of the total number of predicted values, (", totcount,") thus not material."))
[1] "The number of leverage points that are potential outliers is 1143 .  This is 4.09 % of the total number of predicted values, ( 27962 ) thus not material."
#barplot(lev, ylim = c(0, 2*cutlev))
p3<-plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for Barcelona working set")
abline(h = cutlev, col = "red", lty=2)

#cook distance
cookdist<-cooks.distance(workFinal)
#barplot(cookdist, ylim=c(0,1.01), main = "Cook's Distance plot")
#barplot(cookdist, ylim=c(0,0.005), main = "Cook's Distance plot")
#plot(log(cookdist), type="h", ylim=c(0,1.01), main = "Cook's Distance plot")
#abline(h = 1, col = "red")

ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.005,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")

Prepare summary plot for report

# Opening the graphical device
# Customizing the output
pdf("BarcelonaAssumptionCheck1.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
# par(mfrow=c(2,2))
#residuals scatter plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#normality plot
pdf("BarcelonaAssumptionCheck2.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
grid(10,10)
dev.off()
null device 
          1 
#leverage plot
pdf("BarcelonaAssumptionCheck3.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for Barcelona working set")
abline(h = cutlev, col = "red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#Cook's distance plot
pdf("BarcelonaAssumptionCheck4.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.005,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")
dev.off()
null device 
          1 

Use the estimated model to predict the values in the evaluation set

newdata <- evalBarcelona[ c(-2,-13)]
predict.eval <- predict(workFinal, newdata)
plot(x = evalBarcelona$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Barcelona")
abline(a=0, b=1, col="blue")
grid(10,10)

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("BarcelonaEvalActComp.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,1))
plot(x = evalBarcelona$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Barcelona", cex.lab = 0.75)
abline(a=0, b=1, col="blue")
grid(10,10)

# Closing the graphical device
dev.off()
null device 
          1 

Compute the RMSE for the predictions

rootmse <- rmse(evaldata$energy_demand, predict.eval)
print(paste("The root mean square error of the predicted vs actual",nrow(evaldata),"hourly energy_data points for Barcelona over the 2015-2018 timespan is an energy demand difference of",round(rootmse,2),"MWh."))
[1] "The root mean square error of the predicted vs actual 6991 hourly energy_data points for Barcelona over the 2015-2018 timespan is an energy demand difference of 158.46 MWh."
cat("\n")
demandcompdf <- data.frame("Actual (MWh)" = evalBarcelona$energy_demand,"Predicted (MWh)" = predict.eval)
# head(demandcompdf,4L)
cat("\n")
print(paste("For example, the first actual demand value is",round(evaldata$energy_demand[1],2),"MWh and the corresponding predicted value is",round(predict.eval[1],2),"MWh. The percent error between this predicted value relative to the actual value is",round((100*(predict.eval[1] - evaldata$energy_demand[1])/evaldata$energy_demand[1]),2),"%."))
[1] "For example, the first actual demand value is 2949.49 MWh and the corresponding predicted value is 2779.19 MWh. The percent error between this predicted value relative to the actual value is -5.77 %."
cat("\n")
diffpct <- c((abs(evaldata$energy_demand - predict.eval))/evaldata$energy_demand)
print(paste("The mean difference between the",nrow(evaldata),"evaluation data set actual and model predicted values is",round(100*mean(diffpct),2),"%."))
[1] "The mean difference between the 6991 evaluation data set actual and model predicted values is 3.58 %."

Compute and analyze Hat Matrix for Evaluation data set

Obtain X matrix from the model

Predictors <-rownames(summary(workFinal)$coefficients[,])
workpredictors <- as.data.frame(Predictors)
X <- model.matrix(workFinal)
class(X)
[1] "matrix" "array" 
cat("\n")
dim(X)
[1] 27962    21
cat("\n")
#head(X, 4L)

Create x_new matrix using the validation data. Note that the purpose of running the evaluation regression here is to extract all of the correct predictor coefficients.

evalFinal <- lm(energy_demand ~ E_1 + E_25 + temp + humidity 
                + pressure + wind_speed + factor(day_night) 
                + factor(time_band) + factor(season) 
                + factor(weather_main), data = evalBarcelona)
Predictors <-rownames(summary(evalFinal)$coefficients[,])
evalpredictors <- as.data.frame(Predictors)
x_new1 <- model.matrix(evalFinal)
cat("\n")
dim(x_new1)
[1] 6991   20
cat("\n")
#Check if there's a predictor difference between the work and eval matrices
workevalcompare <-anti_join(workpredictors,evalpredictors)
Joining, by = "Predictors"
cat("\n")

When workevalcompare is not Null, run the following lines of code after identifying which column is missing between X and x_new1 (for Barcelona, it was found that “factor(weather_main)4” appears in WorkBarcelona but not in evalBarcelona).

x_new_ <-as.data.frame(cbind(x_new1,rep(0,nrow(x_new1))))
dummy_xnew<- x_new_ %>% relocate(V21,.before = 16) %>% rename("factor(weather_main)4" = V21)
x_new <- as.matrix(dummy_xnew)  
cat("\n")
class(x_new)
[1] "matrix" "array" 
cat("\n")
dim(x_new)
[1] 6991   21
cat("\n")
# head(x_new, 4L)

Compute the hat values for the validation data

h_new_mid <- solve(t(X)%*%X)
dim(h_new_mid)
[1] 21 21
h_new <- x_new%*%h_new_mid%*%t(x_new)
dim(h_new)
[1] 6991 6991

Plot the hatvalues for the data in the evaluation set

#cutlev2 = (2*length(coefficients(evalFinal)))/nrow(evalBarcelona)
# Count and assess the number of leverage values > cut-off
potoutlier2 <- sum(diag(h_new) > cutlev, na.rm=TRUE)
totcount2 = nrow(evalBarcelona)
plot(diag(h_new), type = "h", ylab = "Leverage for the validation data set", ylim = c(0,2*cutlev), main = "Barcelona")
abline(h=cutlev, col="red", lty=2)

print(paste("The number of leverage points that are potential outliers is", potoutlier2,".  This is", round(100*potoutlier2/totcount2,2),"% of the total number of predicted values, (", totcount2,") thus not material."))
[1] "The number of leverage points that are potential outliers is 298 .  This is 4.26 % of the total number of predicted values, ( 6991 ) thus not material."

Side-by-side comparision

par(mfrow=c(1,2))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for the working data set", main = "Barcelona")
abline(h = cutlev, col = "red", lty=2)
plot(diag(h_new), type = "h", ylim = c(0,2*cutlev), ylab = "Leverage for the evaluation data set", main = "Barcelona")
abline(h=cutlev, col="red", lty=2)

print(paste("The leverage cutoff exceedences in the evaluation set is",round(100*potoutlier2/totcount2,2),"% of the total number of observed values (", totcount2,") and is comparable to that of the working set,", round(100*potoutlier/totcount,2),"% of the total number of predicted values (", totcount,")."))
[1] "The leverage cutoff exceedences in the evaluation set is 4.26 % of the total number of observed values ( 6991 ) and is comparable to that of the working set, 4.09 % of the total number of predicted values ( 27962 )."

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("BarcelonaLevComp.pdf",         # File name
    width = 14, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,2))
plot(lev, type="h", ylab = "Leverage", ylim = c(0,2*cutlev), yaxt = 'n', main = "Barcelona Working Data", res =600)
abline(h = cutlev, col = "red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

plot(diag(h_new), type = "h", ylab = "", ylim = c(0,2*cutlev), yaxt = 'n', main = "Barcelona Evaluation Data")
abline(h=cutlev, col="red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

# Closing the graphical device
dev.off()
null device 
          1 

Bilbao Model

evaldata<-read.csv('data/evaluation_Bilbao.csv')
workdata<-read.csv('data/working_Bilbao.csv')
#remove index and time_ID columns from "eval" analysis data frame
evalBilbao<-evaldata[c(-1,-2)] 
#remove index and time_ID columns from "work" analysis data frame
workBilbao<-workdata[c(-1,-2)] 
# head(workBilbao, 4L)
# Correlation matrix for numeric predictors only (categorical predictors excluded)
workcor<-workBilbao[c(-10:-13)]
workcor_<-ggpairs(workcor)
corvalues<- cor(workBilbao)
# corplot2<- corrplot(cor(workcor), method="color", type="full", addCoef.col = "red", tl.col="black",number.cex = 0.75)

Model Processing

workModelBilbao1 <- lm(energy_demand ~ E_1 + E_2 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBilbao)
vif_values <- vif(workModelBilbao1)
workModelBilbaoFull <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBilbao)
vif_values2<- vif(workModelBilbaoFull)
# to obtain residuals
res.fullmodel1 <- residuals(workModelBilbaoFull) 
# to obtain standardized residuals
std.res.fullmodel1 <- rstandard(workModelBilbaoFull) 
# to obtain fitted/predicted values
pred.fullmodel1 <- fitted.values(workModelBilbaoFull) 
par(mfrow=c(1,1))
qqnorm(y = std.res.fullmodel1, main = " Normal Q-Q Plot ",
       xlab = "Theoretical Quantiles", ylab = "Sample Quantiles")
qqline(y = std.res.fullmodel1)

#residual plot
resplotdata1 <- data.frame(std.res.fullmodel1, pred.fullmodel1)
resbf1 <- lm(std.res.fullmodel1 ~ pred.fullmodel1, data = resplotdata1)
plot(x = pred.fullmodel1, y = std.res.fullmodel1, ylab = "Standardized Residuals", xlab = "Predicted Values", main = "Residuals Plot", col = ifelse(std.res.fullmodel1 < -3,"red",ifelse(std.res.fullmodel1 > 3,"red","black")))
abline(h = 0, col="blue", lty=1)
abline(resbf1, col="red", lty=3)
abline(h = 3, col="green", lty=3)
abline(h=-3, col="green", lty=3)
legend("bottomleft", legend=c("Best fit line of standardized residuals", "Horizontal line y = 0.0", "Horizontal line, y = +/- 3"), fill = c("red","blue","green"), cex = 1.0)

# summary(workModelBilbaoFull)
df<- workModelBilbaoFull %>% 
  tidy()
#Get the variables which there p-value larger than 0.05 
df%>%
  filter(df$p.value>0.05)
workModelBilbaoPvalue <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBilbao)
#summary(workModelBilbaoPvalue)

Reduced model comparisons

  • p-value approach
anovacheck1<-anova(workModelBilbaoPvalue, workModelBilbaoFull)
  • Best subset selection method
bestfits <- regsubsets(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBilbao, nbest = 1)
# plot(bestfits, scale="adjr2")
workModelBilbaoBestfit<-lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + rain_duration + factor(day_night) + factor(weather_main), data = workBilbao)
# summary(workModelBilbaoBestfit)
anovacheck2<-anova(workModelBilbaoBestfit, workModelBilbaoPvalue)
  • AIC with forward selection approach test method
workNullModel<- lm(energy_demand ~ 1, data = workBilbao)
step.working <- stepAIC(workNullModel, scope = list(lower = workNullModel,
upper = workModelBilbaoFull), direction = "forward", trace=FALSE)
# summary(step.working)

Same reduced model as P-Value approach

  • Cross Validation test method
# Set seed for reproducibility in CV
set.seed(123)
# Set up repeated k-fold cross-validation, with k=10
train.control <- trainControl(method = "cv", number = 10)
# Train the model
step.cv.work <- train(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBilbao, method = "leapBackward",
tuneGrid = data.frame(nvmax = 1:12),
trControl = train.control)
# step.cv.work$results
# summary(step.cv.work$finalModel)
workModelBilbaoCross <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBilbao)
anovacheck3<-anova( workModelBilbaoCross, workModelBilbaoPvalue)
# summary(workModelBilbaoCross)
  • OLS method
#ModelOLS <- ols_step_all_possible(workModelBilbaoFull)
#cat("\n")
#plot(x = ModelOLS$n, y = ModelOLS$adjr)
#plot(x = ModelOLS$n, y = ModelOLS$aic)
#OLSsummary <- ModelOLS %>% group_by(n) %>%  filter(adjr == max(adjr))
#cat("\n")
#OLSsummary

Same reduced model as P-Value approach (thus commenting out given processing time)

Select the final model

Model adjusted R-square from above methods

  1. Full: 0.9154

  2. P-value: 0.9154

  3. Bestfits: 0.9149

  4. AIC Method: 0.9154

  5. Cross-validation: 0.9154

  6. OLS-step: 0.9154

Final model is the workModelBilbaoPvalue with highest Adjusted R-square and fewest predictors.

Method <- c('Full', 'P-value', 'BestFits', 'AIC Forward', 'Cross Validation','OLS_step')
Variables <- c(13,12,9,12,11,12)
Coefficients <- c(23,22,16,22,21,22)
Adjusted_R2 <- c(0.9153865,0.9153886,0.9148913,0.9153886,0.9153680,0.9153886)
BilbaoTable<-data.frame(Method,Variables,Coefficients,Adjusted_R2)

Build the Final Model

workFinal <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workBilbao)

Check the linearity, heteroscedasticity, outlier and normal assumption

par(mfrow=c(2,2))
#Residual Plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
#norm test
std.res <- rstandard(workFinal)
ad.test(std.res)

    Anderson-Darling normality test

data:  std.res
A = 133, p-value < 0.00000000000000022
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
#leverage 
lev<-hatvalues(workFinal)
cutlev = (2*length(coefficients(workFinal)))/nrow(workBilbao)
# Count and assess the number of leverage values > cut-off
potoutlier <- sum(lev > cutlev, na.rm=TRUE)
totcount = length(fitted(workFinal))
print(paste("The number of leverage points that are potential outliers is", potoutlier,".  This is", round(100*potoutlier/totcount,2),"% of the total number of predicted values, (", totcount,") thus not material."))
[1] "The number of leverage points that are potential outliers is 1860 .  This is 6.66 % of the total number of predicted values, ( 27936 ) thus not material."
#barplot(lev, ylim = c(0, 2*cutlev))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for Bilbao working set")
abline(h = cutlev, col = "red", lty=2)

#cook distance
cookdist<-cooks.distance(workFinal)
#barplot(cookdist, ylim=c(0,1.01), main = "Cook's Distance plot")
#barplot(cookdist, ylim=c(0,0.005), main = "Cook's Distance plot")
#plot((cookdist), type="h", ylim=c(0,0.05), main = "Cook's Distance plot")
#abline(h = 1, col = "red")
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.015,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")

Prepare summary plot for report

# Opening the graphical device
# Customizing the output
pdf("BilbaoAssumptionCheck1.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
# par(mfrow=c(2,2))
#residuals scatter plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#normality plot
pdf("BilbaoAssumptionCheck2.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
grid(10,10)
dev.off()
null device 
          1 
#leverage plot
pdf("BilbaoAssumptionCheck3.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for Barcelona working set")
abline(h = cutlev, col = "red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#Cook's distance plot
pdf("BilbaoAssumptionCheck4.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.005,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")
dev.off()
null device 
          1 

Use the estimated model to predict the values in the evaluation set

newdata <- evalBilbao[ c(-2,-14)]
predict.eval <- predict(workFinal, newdata)
plot(x = evalBilbao$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Bilbao")
abline(a=0, b=1, col="blue")
grid(10,10)

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("BilbaoEvalActComp.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,1))
plot(x = evalBilbao$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Bilbao", cex.lab = 0.75)
abline(a=0, b=1, col="blue")
grid(10,10)

# Closing the graphical device
dev.off()
null device 
          1 

Compute the RMSE for the predictions

rootmse <- rmse(evalBilbao$energy_demand, predict.eval)
print(paste("The root mean square error of the predicted vs actual",nrow(evaldata),"hourly energy_data points for Bilbao over the 2015-2018 timespan is an energy demand difference of",round(rootmse,2),"MWh."))
[1] "The root mean square error of the predicted vs actual 6985 hourly energy_data points for Bilbao over the 2015-2018 timespan is an energy demand difference of 61.98 MWh."
cat("\n")
demandcompdf <- data.frame("Actual (MWh)" = evalBilbao$energy_demand,"Predicted (MWh)" = predict.eval)
# head(demandcompdf,4L)
cat("\n")
print(paste("For example, the first actual demand value is",round(evaldata$energy_demand[1],2),"MWh and the corresponding predicted value is",round(predict.eval[1],2),"MWh. The percent error between this predicted value relative to the actual value is",round((100*(predict.eval[1] - evaldata$energy_demand[1])/evaldata$energy_demand[1]),2),"%."))
[1] "For example, the first actual demand value is 926.33 MWh and the corresponding predicted value is 927.72 MWh. The percent error between this predicted value relative to the actual value is 0.15 %."
cat("\n")
diffpct <- c((abs(evaldata$energy_demand - predict.eval))/evaldata$energy_demand)
print(paste("The mean difference between the",nrow(evaldata),"evaluation data set actual and model predicted values is",round(100*mean(diffpct),2),"%."))
[1] "The mean difference between the 6985 evaluation data set actual and model predicted values is 3.64 %."

Compute and analyze Hat Matrix for Evaluation data set

Obtain X matrix from the model

Predictors <-rownames(summary(workFinal)$coefficients[,])
workpredictors <- as.data.frame(Predictors)
X <- model.matrix(workFinal)
class(X)
[1] "matrix" "array" 
cat("\n")
dim(X)
[1] 27936    22
cat("\n")
# head(X, 4L)

Create x_new matrix using the validation data. Note that the purpose of running the evaluation regression here is to extract all of the correct predictor coefficients.

evalFinal <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = evalBilbao)
Predictors <-rownames(summary(evalFinal)$coefficients[,])
evalpredictors <- as.data.frame(Predictors)
x_new1 <- model.matrix(evalFinal)
cat("\n")
dim(x_new1)
[1] 6985   22
cat("\n")
#Check if there's a predictor difference between the work and eval matrices
workevalcompare <-anti_join(workpredictors,evalpredictors)
Joining, by = "Predictors"

If workevalcompare is ‘No data available in table’, run the next line of code and skip to line 816

x_new <- x_new1

Otherwise when workevalcompare is not ‘No data available in table’, run the following lines of code after identifying which column is missing between X and x_new1 (no discrepency was found between workBilbao but not in evalBilbao).

#x_new_ <-as.data.frame(cbind(x_new1,rep(0,nrow(x_new1))))
#dummy_xnew<- x_new_ %>% relocate(V21,.before = 16) %>% rename("factor(weather_main)4" = V21)
#x_new <- as.matrix(dummy_xnew)  
cat("\n")
class(x_new)
[1] "matrix" "array" 
cat("\n")
dim(x_new)
[1] 6985   22
cat("\n")
# head(x_new, 4L)

Compute the hat values for the validation data

h_new_mid <- solve(t(X)%*%X)
dim(h_new_mid)
[1] 22 22
h_new <- x_new%*%h_new_mid%*%t(x_new)
dim(h_new)
[1] 6985 6985

Plot the hatvalues for the data in the evaluation set

#cutlev2 = (2*length(coefficients(evalFinal)))/nrow(evalBarcelona)
# Count and assess the number of leverage values > cut-off
potoutlier2 <- sum(diag(h_new) > cutlev, na.rm=TRUE)
totcount2 = nrow(evalBilbao)
# plot(diag(h_new), type = "h", ylab = "Leverage for the validation data set", ylim = c(0,2*cutlev), main = "Bilbao")
# abline(h=cutlev, col="red", lty=2)
print(paste("The number of leverage points that are potential outliers is", potoutlier2,".  This is", round(100*potoutlier2/totcount2,2),"% of the total number of predicted values, (", totcount2,") thus not material."))
[1] "The number of leverage points that are potential outliers is 498 .  This is 7.13 % of the total number of predicted values, ( 6985 ) thus not material."

Side-by-side comparision

par(mfrow=c(1,2))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for the working data set", main = "Barcelona")
abline(h = cutlev, col = "red", lty=2)
plot(diag(h_new), type = "h", ylim = c(0,2*cutlev), ylab = "Leverage for the evaluation data set", main = "Barcelona")
abline(h=cutlev, col="red", lty=2)

print(paste("The leverage cutoff exceedences in the evaluation set is",round(100*potoutlier2/totcount2,2),"% of the total number of observed values (", totcount2,") and is comparable to that of the working set,", round(100*potoutlier/totcount,2),"% of the total number of predicted values (", totcount,")."))
[1] "The leverage cutoff exceedences in the evaluation set is 7.13 % of the total number of observed values ( 6985 ) and is comparable to that of the working set, 6.66 % of the total number of predicted values ( 27936 )."

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("BilbaoLevComp.pdf",         # File name
    width = 14, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,2))
plot(lev, type="h", ylab = "Leverage", ylim = c(0,2*cutlev), yaxt = 'n', main = "Bilbao Working Data", res =600)
abline(h = cutlev, col = "red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

plot(diag(h_new), type = "h", ylab = "", ylim = c(0,2*cutlev), yaxt = 'n', main = "Bilbao Evaluation Data")
abline(h=cutlev, col="red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

# Closing the graphical device
dev.off()
null device 
          1 

Madrid Model

evaldata<-read.csv('data/evaluation_Madrid.csv')
workdata<-read.csv('data/working_Madrid.csv')
#remove index and time_ID columns from "eval" analysis dataframe
evalMadrid<-evaldata[c(-1,-2)] 
#remove index and time_ID columns from "work" analysis dataframe
workMadrid<-workdata[c(-1,-2)] 
head(workMadrid, 4L)
# Correlation matrix for numeric predictors only (categorical predictors excluded)
workcor<-workMadrid[c(-10:-13) ]
ggpairs(workcor)

cor(workMadrid)
                        E_1           E_2          E_25         temp     humidity      pressure   wind_speed rain_duration snow_duration    day_night
E_1            1.0000000000  0.9499434705  0.6565882882  0.143391210 -0.156162266  0.0004020366  0.053524514    0.04305124 -0.0009123988 -0.542698752
E_2            0.9499434705  1.0000000000  0.5667056032  0.095664873 -0.104597491  0.0004102668  0.024935983    0.03279678 -0.0003432574 -0.535715015
E_25           0.6565882882  0.5667056032  1.0000000000  0.177936106 -0.211198630 -0.0003217271  0.083479995    0.03982243  0.0099683465 -0.511477237
temp           0.1433912096  0.0956648734  0.1779361065  1.000000000 -0.794717256  0.0239361080  0.069165992   -0.09436353 -0.0050214038 -0.310400429
humidity      -0.1561622663 -0.1045974910 -0.2111986301 -0.794717256  1.000000000 -0.0934723236 -0.115914299    0.20892142  0.0034644595  0.296478951
pressure       0.0004020366  0.0004102668 -0.0003217271  0.023936108 -0.093472324  1.0000000000 -0.153619309   -0.08927302 -0.0018305651  0.010873667
wind_speed     0.0535245136  0.0249359835  0.0834799950  0.069165992 -0.115914299 -0.1536193090  1.000000000    0.13766407  0.0047321116 -0.112761329
rain_duration  0.0430512358  0.0327967771  0.0398224320 -0.094363530  0.208921418 -0.0892730225  0.137664073    1.00000000  0.0252627940 -0.039484943
snow_duration -0.0009123988 -0.0003432574  0.0099683465 -0.005021404  0.003464459 -0.0018305651  0.004732112    0.02526279  1.0000000000 -0.005716835
day_night     -0.5426987515 -0.5357150149 -0.5114772370 -0.310400429  0.296478951  0.0108736668 -0.112761329   -0.03948494 -0.0057168354  1.000000000
time_band      0.1141809926  0.0968003546  0.0859565795 -0.125152565  0.086303385 -0.2942368366  0.045006948    0.00305527 -0.0007100628 -0.003141369
season         0.0832153797  0.0826213909  0.0824320712 -0.367294486  0.316990500  0.0677459079 -0.109874045   -0.05817062 -0.0079750205  0.143257400
weather_main   0.0445935510  0.0373754702  0.0311186482 -0.198277741  0.384072009 -0.1135259629  0.163858653    0.62217661  0.0174843840 -0.044672800
energy_demand  0.9499222286  0.8313872235  0.6847988867  0.184015883 -0.200387099 -0.0002102571  0.080783557    0.04958506 -0.0021141307 -0.509903410
                  time_band       season weather_main energy_demand
E_1            0.1141809926  0.083215380   0.04459355  0.9499222286
E_2            0.0968003546  0.082621391   0.03737547  0.8313872235
E_25           0.0859565795  0.082432071   0.03111865  0.6847988867
temp          -0.1251525653 -0.367294486  -0.19827774  0.1840158834
humidity       0.0863033852  0.316990500   0.38407201 -0.2003870994
pressure      -0.2942368366  0.067745908  -0.11352596 -0.0002102571
wind_speed     0.0450069477 -0.109874045   0.16385865  0.0807835573
rain_duration  0.0030552696 -0.058170615   0.62217661  0.0495850577
snow_duration -0.0007100628 -0.007975021   0.01748438 -0.0021141307
day_night     -0.0031413686  0.143257400  -0.04467280 -0.5099034101
time_band      1.0000000000  0.161864278   0.01049282  0.1256833010
season         0.1618642784  1.000000000   0.03949142  0.0841398021
weather_main   0.0104928162  0.039491421   1.00000000  0.0471215127
energy_demand  0.1256833010  0.084139802   0.04712151  1.0000000000
corrplot(cor(workcor), method="color", type="full", addCoef.col = "red", tl.col="black",number.cex = 0.75)

workModelMadrid1 <- lm(energy_demand ~ E_1 + E_2 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workMadrid)
vif(workModelMadrid1)
                          GVIF Df GVIF^(1/(2*Df))
E_1                  13.683412  1        3.699110
E_2                  11.842865  1        3.441346
E_25                  2.066652  1        1.437586
temp                  4.852956  1        2.202943
humidity              3.756247  1        1.938104
pressure              1.202259  1        1.096476
wind_speed            1.225852  1        1.107182
rain_duration         2.189530  1        1.479706
snow_duration         1.001029  1        1.000515
factor(day_night)     1.806033  1        1.343887
factor(time_band)     1.206976  2        1.048153
factor(season)        3.038317  3        1.203480
factor(weather_main)  3.422558  8        1.079933
workModelMadridFull <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workMadrid)
vif(workModelMadridFull)
                         GVIF Df GVIF^(1/(2*Df))
E_1                  2.004683  1        1.415868
E_25                 1.935699  1        1.391294
temp                 4.775330  1        2.185253
humidity             3.745110  1        1.935229
pressure             1.201910  1        1.096316
wind_speed           1.223651  1        1.106187
rain_duration        2.189374  1        1.479653
snow_duration        1.001006  1        1.000503
factor(day_night)    1.732418  1        1.316213
factor(time_band)    1.202172  2        1.047108
factor(season)       2.970957  3        1.198991
factor(weather_main) 3.414880  8        1.079782
# to obtain residuals
res.fullmodel1 <- residuals(workModelMadridFull) 
# to obtain standardized residuals
std.res.fullmodel1 <- rstandard(workModelMadridFull) 
# to obtain fitted/predicted values
pred.fullmodel1 <- fitted.values(workModelMadridFull) 
par(mfrow=c(1,1))
qqnorm(y = std.res.fullmodel1, main = " Normal Q-Q Plot ",
       xlab = "Theoretical Quantiles", ylab = "Sample Quantiles")
qqline(y = std.res.fullmodel1)

#residual plot
resplotdata1 <- data.frame(std.res.fullmodel1, pred.fullmodel1)
resbf1 <- lm(std.res.fullmodel1 ~ pred.fullmodel1, data = resplotdata1)
plot(x = pred.fullmodel1, y = std.res.fullmodel1, ylab = "Standardized Residuals", xlab = "Predicted Values", main = "Residuals Plot", col = ifelse(std.res.fullmodel1 < -3,"red",ifelse(std.res.fullmodel1 > 3,"red","black")))
abline(h = 0, col="blue", lty=1)
abline(resbf1, col="red", lty=3)
abline(h = 3, col="green", lty=3)
abline(h=-3, col="green", lty=3)
legend("bottomleft", legend=c("Best fit line of standardized residuals", "Horizontal line y = 0.0", "Horizontal line, y = +/- 3"), fill = c("red","blue","green"), cex = 0.5)

summary(workModelMadridFull)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    wind_speed + rain_duration + snow_duration + factor(day_night) + 
    factor(time_band) + factor(season) + factor(weather_main), 
    data = workMadrid)

Residuals:
     Min       1Q   Median       3Q      Max 
-1097.14   -56.10     9.01    63.40   914.40 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -1093.188294    59.986245 -18.224 < 0.0000000000000002 ***
E_1                        0.892155     0.002464 362.140 < 0.0000000000000002 ***
E_25                       0.103032     0.002424  42.513 < 0.0000000000000002 ***
temp                       3.432903     0.150672  22.784 < 0.0000000000000002 ***
humidity                  -0.563259     0.049087 -11.475 < 0.0000000000000002 ***
pressure                   0.121147     0.034840   3.477             0.000507 ***
wind_speed                 2.491848     0.355546   7.009     0.00000000000246 ***
rain_duration             13.671539     4.609904   2.966             0.003023 ** 
snow_duration           -124.613076   100.661587  -1.238             0.215749    
factor(day_night)2        42.023414     1.669358  25.173 < 0.0000000000000002 ***
factor(time_band)2         8.612174     8.811459   0.977             0.328388    
factor(time_band)3        74.552399     6.956330  10.717 < 0.0000000000000002 ***
factor(season)2          -48.870952     2.298821 -21.259 < 0.0000000000000002 ***
factor(season)3           -7.374987     1.870953  -3.942     0.00008105907428 ***
factor(season)4           25.722059     2.070951  12.420 < 0.0000000000000002 ***
factor(weather_main)2      8.527549     1.568031   5.438     0.00000005422072 ***
factor(weather_main)3     14.908620     6.279141   2.374             0.017589 *  
factor(weather_main)5      4.942025     5.673912   0.871             0.383757    
factor(weather_main)6     -7.593469    27.311364  -0.278             0.780989    
factor(weather_main)7     18.121717     5.063404   3.579             0.000346 ***
factor(weather_main)8     27.082310     3.935399   6.882     0.00000000000604 ***
factor(weather_main)10    40.922832    18.759636   2.181             0.029160 *  
factor(weather_main)12    32.889632     9.415102   3.493             0.000478 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 105.5 on 27740 degrees of freedom
Multiple R-squared:  0.9161,    Adjusted R-squared:  0.916 
F-statistic: 1.377e+04 on 22 and 27740 DF,  p-value: < 0.00000000000000022
df<- workModelMadridFull %>% 
  tidy()
#Get the variables which there p-value larger than 0.05 
df%>%
  filter(df$p.value>0.05)
workModelMadridPvalue <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workMadrid)
summary(workModelMadridPvalue)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    wind_speed + rain_duration + factor(day_night) + factor(time_band) + 
    factor(season) + factor(weather_main), data = workMadrid)

Residuals:
     Min       1Q   Median       3Q      Max 
-1097.12   -56.13     9.00    63.40   914.46 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -1093.581173    59.985981 -18.231 < 0.0000000000000002 ***
E_1                        0.892186     0.002463 362.168 < 0.0000000000000002 ***
E_25                       0.102993     0.002423  42.500 < 0.0000000000000002 ***
temp                       3.434373     0.150669  22.794 < 0.0000000000000002 ***
humidity                  -0.562954     0.049087 -11.468 < 0.0000000000000002 ***
pressure                   0.121104     0.034840   3.476             0.000510 ***
wind_speed                 2.492271     0.355549   7.010     0.00000000000244 ***
rain_duration             13.595711     4.609541   2.949             0.003186 ** 
factor(day_night)2        42.030231     1.669365  25.177 < 0.0000000000000002 ***
factor(time_band)2         8.614108     8.811544   0.978             0.328284    
factor(time_band)3        74.551969     6.956397  10.717 < 0.0000000000000002 ***
factor(season)2          -48.869024     2.298842 -21.258 < 0.0000000000000002 ***
factor(season)3           -7.363501     1.870948  -3.936     0.00008315458019 ***
factor(season)4           25.742089     2.070908  12.430 < 0.0000000000000002 ***
factor(weather_main)2      8.525961     1.568045   5.437     0.00000005454471 ***
factor(weather_main)3     14.917369     6.279198   2.376             0.017523 *  
factor(weather_main)5      4.936703     5.673965   0.870             0.384274    
factor(weather_main)6     -7.601158    27.311626  -0.278             0.780775    
factor(weather_main)7     18.113599     5.063448   3.577             0.000348 ***
factor(weather_main)8     27.056170     3.935380   6.875     0.00000000000633 ***
factor(weather_main)10    40.938710    18.759812   2.182             0.029099 *  
factor(weather_main)12    32.907720     9.415181   3.495             0.000474 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 105.5 on 27741 degrees of freedom
Multiple R-squared:  0.9161,    Adjusted R-squared:  0.916 
F-statistic: 1.442e+04 on 21 and 27741 DF,  p-value: < 0.00000000000000022
  • p-value approach
anova(workModelMadridPvalue, workModelMadridFull)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + snow_duration + factor(day_night) + factor(time_band) + 
    factor(season) + factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1  27741 308998752                           
2  27740 308981682  1     17070 1.5325 0.2157
  • Best subset selection method
bestfits <- regsubsets(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workMadrid, nbest = 1)
plot(bestfits, scale="adjr2")

workModelMadridBestfit<-lm(energy_demand ~ E_1 + E_25 + temp + wind_speed + factor(day_night) + factor(time_band) + factor(season) , data = workMadrid)
summary(workModelMadridBestfit)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + wind_speed + 
    factor(day_night) + factor(time_band) + factor(season), data = workMadrid)

Residuals:
     Min       1Q   Median       3Q      Max 
-1084.09   -56.96     9.10    64.09   916.61 

Coefficients:
                       Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)        -1293.374957    32.392614 -39.928 < 0.0000000000000002 ***
E_1                    0.892776     0.002471 361.305 < 0.0000000000000002 ***
E_25                   0.106053     0.002420  43.822 < 0.0000000000000002 ***
temp                   4.427351     0.112498  39.355 < 0.0000000000000002 ***
wind_speed             3.789287     0.332289  11.404 < 0.0000000000000002 ***
factor(day_night)2    40.399494     1.662907  24.294 < 0.0000000000000002 ***
factor(time_band)2     1.913481     8.661304   0.221                0.825    
factor(time_band)3    67.719513     6.725624  10.069 < 0.0000000000000002 ***
factor(season)2      -52.144538     2.274335 -22.927 < 0.0000000000000002 ***
factor(season)3       -9.819047     1.859339  -5.281          0.000000129 ***
factor(season)4       23.902070     2.030660  11.771 < 0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 106 on 27752 degrees of freedom
Multiple R-squared:  0.9154,    Adjusted R-squared:  0.9154 
F-statistic: 3.002e+04 on 10 and 27752 DF,  p-value: < 0.00000000000000022
anova(workModelMadridBestfit, workModelMadridPvalue)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + wind_speed + factor(day_night) + 
    factor(time_band) + factor(season)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F                Pr(>F)    
1  27752 311591757                                              
2  27741 308998752 11   2593005 21.163 < 0.00000000000000022 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
  • AIC with forward selection approach test method
workNullModel<- lm(energy_demand ~ 1, data = workMadrid)
step.working <- stepAIC(workNullModel, scope = list(lower = workNullModel,
upper = workModelMadridFull), direction = "forward", trace=FALSE)
summary(step.working)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + humidity + factor(day_night) + 
    factor(weather_main) + factor(season) + temp + factor(time_band) + 
    wind_speed + pressure + rain_duration, data = workMadrid)

Residuals:
     Min       1Q   Median       3Q      Max 
-1097.12   -56.13     9.00    63.40   914.46 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -1093.581173    59.985981 -18.231 < 0.0000000000000002 ***
E_1                        0.892186     0.002463 362.168 < 0.0000000000000002 ***
E_25                       0.102993     0.002423  42.500 < 0.0000000000000002 ***
humidity                  -0.562954     0.049087 -11.468 < 0.0000000000000002 ***
factor(day_night)2        42.030231     1.669365  25.177 < 0.0000000000000002 ***
factor(weather_main)2      8.525961     1.568045   5.437     0.00000005454471 ***
factor(weather_main)3     14.917369     6.279198   2.376             0.017523 *  
factor(weather_main)5      4.936703     5.673965   0.870             0.384274    
factor(weather_main)6     -7.601158    27.311626  -0.278             0.780775    
factor(weather_main)7     18.113599     5.063448   3.577             0.000348 ***
factor(weather_main)8     27.056170     3.935380   6.875     0.00000000000633 ***
factor(weather_main)10    40.938710    18.759812   2.182             0.029099 *  
factor(weather_main)12    32.907720     9.415181   3.495             0.000474 ***
factor(season)2          -48.869024     2.298842 -21.258 < 0.0000000000000002 ***
factor(season)3           -7.363501     1.870948  -3.936     0.00008315458019 ***
factor(season)4           25.742089     2.070908  12.430 < 0.0000000000000002 ***
temp                       3.434373     0.150669  22.794 < 0.0000000000000002 ***
factor(time_band)2         8.614108     8.811544   0.978             0.328284    
factor(time_band)3        74.551969     6.956397  10.717 < 0.0000000000000002 ***
wind_speed                 2.492271     0.355549   7.010     0.00000000000244 ***
pressure                   0.121104     0.034840   3.476             0.000510 ***
rain_duration             13.595711     4.609541   2.949             0.003186 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 105.5 on 27741 degrees of freedom
Multiple R-squared:  0.9161,    Adjusted R-squared:  0.916 
F-statistic: 1.442e+04 on 21 and 27741 DF,  p-value: < 0.00000000000000022

Same reduced model as P-value

  • Cross Validation test method
# Set seed for reproducibility in CV
set.seed(123)
# Set up repeated k-fold cross-validation, with k=10
train.control <- trainControl(method = "cv", number = 10)
# Train the model
step.cv.work <- train(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workMadrid, method = "leapBackward",
tuneGrid = data.frame(nvmax = 1:12),
trControl = train.control)
Reordering variables and trying again:
step.cv.work$results
summary(step.cv.work$finalModel)
Subset selection object
22 Variables  (and intercept)
                       Forced in Forced out
E_1                        FALSE      FALSE
E_25                       FALSE      FALSE
temp                       FALSE      FALSE
humidity                   FALSE      FALSE
pressure                   FALSE      FALSE
wind_speed                 FALSE      FALSE
rain_duration              FALSE      FALSE
snow_duration              FALSE      FALSE
factor(day_night)2         FALSE      FALSE
factor(time_band)2         FALSE      FALSE
factor(time_band)3         FALSE      FALSE
factor(season)2            FALSE      FALSE
factor(season)3            FALSE      FALSE
factor(season)4            FALSE      FALSE
factor(weather_main)2      FALSE      FALSE
factor(weather_main)3      FALSE      FALSE
factor(weather_main)5      FALSE      FALSE
factor(weather_main)6      FALSE      FALSE
factor(weather_main)7      FALSE      FALSE
factor(weather_main)8      FALSE      FALSE
factor(weather_main)10     FALSE      FALSE
factor(weather_main)12     FALSE      FALSE
1 subsets of each size up to 12
Selection Algorithm: backward
          E_1 E_25 temp humidity pressure wind_speed rain_duration snow_duration factor(day_night)2 factor(time_band)2 factor(time_band)3 factor(season)2
1  ( 1 )  "*" " "  " "  " "      " "      " "        " "           " "           " "                " "                " "                " "            
2  ( 1 )  "*" "*"  " "  " "      " "      " "        " "           " "           " "                " "                " "                " "            
3  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           " "                " "                " "                " "            
4  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                " "                " "            
5  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                " "                "*"            
6  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                " "                "*"            
7  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                " "                "*"            
8  ( 1 )  "*" "*"  "*"  "*"      " "      " "        " "           " "           "*"                " "                " "                "*"            
9  ( 1 )  "*" "*"  "*"  "*"      " "      " "        " "           " "           "*"                " "                "*"                "*"            
10  ( 1 ) "*" "*"  "*"  "*"      " "      "*"        " "           " "           "*"                " "                "*"                "*"            
11  ( 1 ) "*" "*"  "*"  "*"      " "      "*"        " "           " "           "*"                " "                "*"                "*"            
12  ( 1 ) "*" "*"  "*"  "*"      " "      "*"        "*"           " "           "*"                " "                "*"                "*"            
          factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)5 factor(weather_main)6 factor(weather_main)7
1  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
2  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
3  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
4  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
5  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
6  ( 1 )  " "             "*"             " "                   " "                   " "                   " "                   " "                  
7  ( 1 )  " "             "*"             " "                   " "                   " "                   " "                   " "                  
8  ( 1 )  " "             "*"             " "                   " "                   " "                   " "                   " "                  
9  ( 1 )  " "             "*"             " "                   " "                   " "                   " "                   " "                  
10  ( 1 ) " "             "*"             " "                   " "                   " "                   " "                   " "                  
11  ( 1 ) " "             "*"             "*"                   " "                   " "                   " "                   " "                  
12  ( 1 ) " "             "*"             "*"                   " "                   " "                   " "                   " "                  
          factor(weather_main)8 factor(weather_main)10 factor(weather_main)12
1  ( 1 )  " "                   " "                    " "                   
2  ( 1 )  " "                   " "                    " "                   
3  ( 1 )  " "                   " "                    " "                   
4  ( 1 )  " "                   " "                    " "                   
5  ( 1 )  " "                   " "                    " "                   
6  ( 1 )  " "                   " "                    " "                   
7  ( 1 )  "*"                   " "                    " "                   
8  ( 1 )  "*"                   " "                    " "                   
9  ( 1 )  "*"                   " "                    " "                   
10  ( 1 ) "*"                   " "                    " "                   
11  ( 1 ) "*"                   " "                    " "                   
12  ( 1 ) "*"                   " "                    " "                   
workModelMadridCross <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workMadrid)
anova( workModelMadridCross, workModelMadridPvalue)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + wind_speed + rain_duration + 
    factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F    Pr(>F)    
1  27742 309133337                                  
2  27741 308998752  1    134585 12.083 0.0005097 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
summary(workModelMadridCross)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main), data = workMadrid)

Residuals:
     Min       1Q   Median       3Q      Max 
-1097.70   -56.04     8.94    63.55   915.46 

Coefficients:
                          Estimate  Std. Error t value             Pr(>|t|)    
(Intercept)            -957.033754   45.343201 -21.106 < 0.0000000000000002 ***
E_1                       0.892368    0.002463 362.251 < 0.0000000000000002 ***
E_25                      0.102965    0.002424  42.480 < 0.0000000000000002 ***
temp                      3.389425    0.150143  22.575 < 0.0000000000000002 ***
humidity                 -0.583777    0.048730 -11.980 < 0.0000000000000002 ***
wind_speed                2.360256    0.353585   6.675      0.0000000000252 ***
rain_duration            14.273068    4.606340   3.099             0.001947 ** 
factor(day_night)2       42.044434    1.669693  25.181 < 0.0000000000000002 ***
factor(time_band)2        2.621027    8.642947   0.303             0.761697    
factor(time_band)3       68.228323    6.715646  10.160 < 0.0000000000000002 ***
factor(season)2         -48.456952    2.296242 -21.103 < 0.0000000000000002 ***
factor(season)3          -6.874955    1.866034  -3.684             0.000230 ***
factor(season)4          26.410474    2.062374  12.806 < 0.0000000000000002 ***
factor(weather_main)2     8.205598    1.565647   5.241      0.0000001608532 ***
factor(weather_main)3    14.948023    6.280446   2.380             0.017315 *  
factor(weather_main)5     6.066888    5.665773   1.071             0.284270    
factor(weather_main)6    -7.420466   27.317031  -0.272             0.785899    
factor(weather_main)7    19.151012    5.055654   3.788             0.000152 ***
factor(weather_main)8    25.713836    3.917169   6.564      0.0000000000532 ***
factor(weather_main)10   40.421348   18.762968   2.154             0.031224 *  
factor(weather_main)12   33.431946    9.415854   3.551             0.000385 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 105.6 on 27742 degrees of freedom
Multiple R-squared:  0.9161,    Adjusted R-squared:  0.916 
F-statistic: 1.514e+04 on 20 and 27742 DF,  p-value: < 0.00000000000000022
  • OLS method
#ModelOLS <- ols_step_all_possible(workModelMadridFull)
#cat("\n")
#plot(x = ModelOLS$n, y = ModelOLS$adjr)
#plot(x = ModelOLS$n, y = ModelOLS$aic)
#OLSsummary <- ModelOLS %>% group_by(n) %>%  filter(adjr == max(adjr))
#OLSsummary

Same reduced model as P-Value approach (thus commenting out given processing time)

Select the final model

Model adjusted R-square from above methods (to four significant digits)

  1. Full: 0.916

  2. P-value: 0.916

  3. Bestfits: 0.9154

  4. AIC Method: 0.916

  5. Cross-validation: 0.916

  6. OLS-step: 0.916

Final model is the workModelMadridPvalue with highest Adjusted R-square and fewest predictors.

Method <- c('Full', 'P-value', 'BestFits', 'AIC Forward', 'Cross Validation')
Variables <- c(13,12,8,12,11)
Coefficients <- c(23,22,11,22,21)
Adjusted_R2 <- c(0.9160325,0.9160309,0.9153598,0.9160309,0.9159974)
MadridTable<-data.frame(Method,Variables,Coefficients,Adjusted_R2)

Build the Final Model

workFinal <- workModelMadridPvalue

Check the linearity, heteroscedasticity, outlier and normal assumption

par(mfrow=c(2,2))
#Residual Plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
#norm test
std.res <- rstandard(workFinal)
ad.test(std.res)

    Anderson-Darling normality test

data:  std.res
A = 147.61, p-value < 0.00000000000000022
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
#leverage 
lev<-hatvalues(workFinal)
cutlev = (2*length(coefficients(workFinal)))/nrow(workMadrid)
# Count and assess the number of leverage values > cut-off
potoutlier <- sum(lev > cutlev, na.rm=TRUE)
totcount = length(fitted(workFinal))
print(paste("The number of leverage points that are potential outliers is", potoutlier,".  This is", round(100*potoutlier/totcount,2),"% of the total number of predicted values, (", totcount,") thus not material."))
[1] "The number of leverage points that are potential outliers is 1876 .  This is 6.76 % of the total number of predicted values, ( 27763 ) thus not material."
#barplot(lev, ylim = c(0, 2*cutlev))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for the working set")
abline(h = cutlev, col = "red", lty=2)

#cook distance
cookdist<-cooks.distance(workFinal)
#barplot(cookdist, ylim=c(0,1.01), main = "Cook's Distance plot")
#abline(h = 1, col = "red")
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.01,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")

# Opening the graphical device
# Customizing the output
pdf("MadridAssumptionCheck1.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
# par(mfrow=c(2,2))
#residuals scatter plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#normality plot
pdf("MadridAssumptionCheck2.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
grid(10,10)
dev.off()
null device 
          1 
#leverage plot
pdf("MadridAssumptionCheck3.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for Barcelona working set")
abline(h = cutlev, col = "red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#Cook's distance plot
pdf("MadridAssumptionCheck4.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.005,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")
dev.off()
null device 
          1 

Use the estimated model to predict the values in the evaluation set

newdata <- evalMadrid[ c(-2,-14)]
predict.eval <- predict(workFinal, newdata)
plot(x = evalMadrid$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Madrid")
abline(a=0, b=1, col="blue")
grid(10,10)

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("MadridEvalActComp.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,1))
plot(x = evalMadrid$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Madrid", cex.lab = 0.75)
abline(a=0, b=1, col="blue")
grid(10,10)

# Closing the graphical device
dev.off()
null device 
          1 

Compute the RMSE for the predictions

rootmse <- rmse(evalMadrid$energy_demand, predict.eval)
rootmse <- rmse(evaldata$energy_demand, predict.eval)
print(paste("The root mean square error of the predicted vs actual",nrow(evaldata),"hourly energy_data points for Madrid over the 2015-2018 timespan is an energy demand difference of",round(rootmse,2),"MWh."))
[1] "The root mean square error of the predicted vs actual 6941 hourly energy_data points for Madrid over the 2015-2018 timespan is an energy demand difference of 107.04 MWh."
cat("\n")
demandcompdf <- data.frame("Actual (MWh)" = evalMadrid$energy_demand,"Predicted (MWh)" = predict.eval)
head(demandcompdf,4L)
cat("\n")
print(paste("For example, the first actual demand value is",round(evaldata$energy_demand[1],2),"MWh and the corresponding predicted value is",round(predict.eval[1],2),"MWh. The percent error between this predicted value relative to the actual value is",round((100*(predict.eval[1] - evaldata$energy_demand[1])/evaldata$energy_demand[1]),2),"%."))
[1] "For example, the first actual demand value is 1920.23 MWh and the corresponding predicted value is 2030.71 MWh. The percent error between this predicted value relative to the actual value is 5.75 %."
cat("\n")
diffpct <- c((abs(evaldata$energy_demand - predict.eval))/evaldata$energy_demand)
print(paste("The mean difference between the",nrow(evaldata),"evaluation data set actual and model predicted values is",round(100*mean(diffpct),2),"%."))
[1] "The mean difference between the 6941 evaluation data set actual and model predicted values is 3.57 %."

Compute and analyze Hat Matrix for Evaluation data set

Obtain X matrix from the model

Predictors <-rownames(summary(workFinal)$coefficients[,])
workpredictors <- as.data.frame(Predictors)
X <- model.matrix(workFinal)
class(X)
[1] "matrix" "array" 
cat("\n")
dim(X)
[1] 27763    22
cat("\n")
head(X, 4L)
  (Intercept)      E_1     E_25     temp humidity pressure wind_speed rain_duration factor(day_night)2 factor(time_band)2 factor(time_band)3
1           1 1920.228 2132.596 281.5740       61      974          1             0                  0                  0                  1
2           1 1963.115 2018.017 281.5740       61      974          1             0                  0                  0                  1
3           1 1872.089 1970.913 282.5377       47     1036          1             0                  0                  1                  0
4           1 1839.466 1949.509 283.3083       45     1035          1             0                  0                  1                  0
  factor(season)2 factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)5 factor(weather_main)6
1               0               0               1                     0                     0                     0                     0
2               0               0               1                     0                     0                     0                     0
3               0               0               1                     0                     0                     0                     0
4               0               0               1                     0                     0                     0                     0
  factor(weather_main)7 factor(weather_main)8 factor(weather_main)10 factor(weather_main)12
1                     0                     0                      0                      0
2                     0                     0                      0                      0
3                     0                     0                      0                      0
4                     0                     0                      0                      0

Create x_new matrix using the validation data. Note that the purpose of running the evaluation regression here is to extract all of the correct predictor coefficients.

evalFinal <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main), data = evalMadrid)
Predictors <-rownames(summary(evalFinal)$coefficients[,])
evalpredictors <- as.data.frame(Predictors)
x_new1 <- model.matrix(evalFinal)
cat("\n")
dim(x_new1)
[1] 6941   22
cat("\n")
#Check if there's a predictor difference between the work and eval matrices
workevalcompare <-anti_join(workpredictors,evalpredictors)
Joining, by = "Predictors"
cat("\n")
#If workevalcompare is 'No data available in table', run the next line of code and skip to line 816
x_new <- x_new1
#Otherwise when workevalcompare is not 'No data available in table', run the following lines of code after identifying which column is missing between X and x_new1 (no discrepancy was found between workMadrid but not in evalMadrid).
#x_new_ <-as.data.frame(cbind(x_new1,rep(0,nrow(x_new1))))
#dummy_xnew<- x_new_ %>% relocate(V21,.before = 16) %>% rename("factor(weather_main)4" = V21)
#x_new <- as.matrix(dummy_xnew)  
cat("\n")
class(x_new)
[1] "matrix" "array" 
cat("\n")
dim(x_new)
[1] 6941   22
cat("\n")
head(x_new, 4L)
  (Intercept)      E_1     E_25     temp humidity pressure wind_speed rain_duration factor(day_night)2 factor(time_band)2 factor(time_band)3
1           1 1966.377 2101.883 281.5740       61      974          1             0                  0                  0                  1
2           1 2229.511 2362.708 277.4590       72     1036          1             0                  1                  0                  1
3           1 2308.602 2377.031 274.9512       55      999          1             0                  1                  0                  1
4           1 2311.068 2286.880 282.5917       57     1037          1             0                  0                  1                  0
  factor(season)2 factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)5 factor(weather_main)6
1               0               0               1                     0                     0                     0                     0
2               0               0               1                     0                     0                     0                     0
3               0               0               1                     0                     0                     0                     0
4               0               0               1                     0                     0                     0                     0
  factor(weather_main)7 factor(weather_main)8 factor(weather_main)10 factor(weather_main)12
1                     0                     0                      0                      0
2                     0                     0                      0                      0
3                     0                     0                      0                      0
4                     0                     0                      0                      0

Compute the hat values for the validation data

h_new_mid <- solve(t(X)%*%X)
dim(h_new_mid)
[1] 22 22
h_new <- x_new%*%h_new_mid%*%t(x_new)
dim(h_new)
[1] 6941 6941

Plot the hatvalues for the data in the evaluation set

#cutlev2 = (2*length(coefficients(evalFinal)))/nrow(evalMadrid)
# Count and assess the number of leverage values > cut-off
potoutlier2 <- sum(diag(h_new) > cutlev, na.rm=TRUE)
totcount2 = nrow(evalMadrid)
plot(diag(h_new), type = "h", ylab = "Leverage for the validation data set", ylim = c(0,2*cutlev), main = "Madrid")
abline(h=cutlev, col="red", lty=2)

print(paste("The number of leverage points that are potential outliers is", potoutlier2,".  This is", round(100*potoutlier2/totcount2,2),"% of the total number of predicted values, (", totcount2,") thus not material."))
[1] "The number of leverage points that are potential outliers is 438 .  This is 6.31 % of the total number of predicted values, ( 6941 ) thus not material."

Side-by-side comparision

par(mfrow=c(1,2))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for the working data set", main = "Madrid")
abline(h = cutlev, col = "red", lty=2)
plot(diag(h_new), type = "h", ylim = c(0,2*cutlev), ylab = "Leverage for the evaluation data set", main = "Madrid")
abline(h=cutlev, col="red", lty=2)

print(paste("The leverage cutoff exceedences in the evaluation set is",round(100*potoutlier2/totcount2,2),"% of the total number of observed values (", totcount2,") and is comparable to that of the working set,", round(100*potoutlier/totcount,2),"% of the total number of predicted values (", totcount,")."))
[1] "The leverage cutoff exceedences in the evaluation set is 6.31 % of the total number of observed values ( 6941 ) and is comparable to that of the working set, 6.76 % of the total number of predicted values ( 27763 )."

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("MadridLevComp.pdf",         # File name
    width = 14, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,2))
plot(lev, type="h", ylab = "Leverage", ylim = c(0,2*cutlev), yaxt = 'n', main = "Madrid Working Data", res =600)
abline(h = cutlev, col = "red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

plot(diag(h_new), type = "h", ylab = "", ylim = c(0,2*cutlev), yaxt = 'n', main = "Madrid Evaluation Data")
abline(h=cutlev, col="red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

# Closing the graphical device
dev.off()
null device 
          1 

Seville Model

evaldata<-read.csv('data/evaluation_Seville.csv')
workdata<-read.csv('data/working_Seville.csv')
#remove index, time_ID and snow_duration columns from "eval" analysis dataframe
evalSeville<-evaldata[c(-1,-2,-11)] 
#remove index, time_ID and snow_duration columns from "work" analysis dataframe
workSeville<-workdata[c(-1,-2,-11)] 
head(workSeville, 4L)
# Correlation matrix for numeric predictors only (categorical predictors excluded)
workcor<-workSeville[c(-9:-12) ]
ggpairs(workcor)

cor(workSeville)
                       E_1          E_2        E_25        temp    humidity    pressure  wind_speed rain_duration    day_night    time_band      season
E_1            1.000000000  0.950854151  0.66638363  0.16537305 -0.23468751 -0.02311708  0.09616216   0.007157484 -0.542317356  0.111533226  0.08079501
E_2            0.950854151  1.000000000  0.57019030  0.11747631 -0.17392319 -0.02373721  0.04825869   0.007868531 -0.534651817  0.091257807  0.08074571
E_25           0.666383630  0.570190303  1.00000000  0.20343503 -0.28989939 -0.03106706  0.14126341   0.017526469 -0.510520679  0.112851400  0.07745398
temp           0.165373052  0.117476309  0.20343503  1.00000000 -0.73635552 -0.36378957  0.13787943  -0.099193770 -0.366474926 -0.147607803 -0.36184280
humidity      -0.234687511 -0.173923187 -0.28989939 -0.73635552  1.00000000  0.17368694 -0.20591888   0.190535534  0.390079509  0.085354419  0.25243795
pressure      -0.023117084 -0.023737213 -0.03106706 -0.36378957  0.17368694  1.00000000 -0.20855776  -0.187508867  0.068019733  0.188780617  0.41474625
wind_speed     0.096162157  0.048258690  0.14126341  0.13787943 -0.20591888 -0.20855776  1.00000000   0.121015082 -0.175701943  0.056228332 -0.08705692
rain_duration  0.007157484  0.007868531  0.01752647 -0.09919377  0.19053553 -0.18750887  0.12101508   1.000000000 -0.009750599  0.000193738 -0.04631036
day_night     -0.542317356 -0.534651817 -0.51052068 -0.36647493  0.39007951  0.06801973 -0.17570194  -0.009750599  1.000000000  0.001903622  0.15151639
time_band      0.111533226  0.091257807  0.11285140 -0.14760780  0.08535442  0.18878062  0.05622833   0.000193738  0.001903622  1.000000000  0.16386623
season         0.080795009  0.080745711  0.07745398 -0.36184280  0.25243795  0.41474625 -0.08705692  -0.046310357  0.151516388  0.163866226  1.00000000
weather_main  -0.004452486  0.011227066 -0.02136187 -0.19715308  0.35226130 -0.16600812  0.09555784   0.572527418  0.025237816  0.006401385  0.02450594
energy_demand  0.950980801  0.832402107  0.69960726  0.20376123 -0.28491923 -0.02150207  0.13592024   0.006346723 -0.509531109  0.123373405  0.08048677
              weather_main energy_demand
E_1           -0.004452486   0.950980801
E_2            0.011227066   0.832402107
E_25          -0.021361874   0.699607258
temp          -0.197153079   0.203761230
humidity       0.352261299  -0.284919235
pressure      -0.166008124  -0.021502073
wind_speed     0.095557837   0.135920236
rain_duration  0.572527418   0.006346723
day_night      0.025237816  -0.509531109
time_band      0.006401385   0.123373405
season         0.024505939   0.080486767
weather_main   1.000000000  -0.022175596
energy_demand -0.022175596   1.000000000
corrplot(cor(workcor), method="color", type="full", addCoef.col = "red", tl.col="black",number.cex = 0.75)

workModelSeville1 <- lm(energy_demand ~ E_1 + E_2 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workSeville)
vif(workModelSeville1)
                          GVIF Df GVIF^(1/(2*Df))
E_1                  14.477427  1        3.804921
E_2                  12.448458  1        3.528237
E_25                  2.162420  1        1.470517
temp                  3.784144  1        1.945288
humidity              2.931415  1        1.712138
pressure              1.604205  1        1.266572
wind_speed            1.210197  1        1.100089
rain_duration         2.059981  1        1.435263
factor(day_night)     1.928208  1        1.388599
factor(time_band)     1.116888  2        1.028022
factor(season)        2.801229  3        1.187294
factor(weather_main)  2.926601 10        1.055160
workModelSevilleFull <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workSeville)
vif(workModelSevilleFull)
                         GVIF Df GVIF^(1/(2*Df))
E_1                  2.048562  1        1.431280
E_25                 1.994263  1        1.412184
temp                 3.726333  1        1.930371
humidity             2.924283  1        1.710053
pressure             1.596200  1        1.263408
wind_speed           1.195560  1        1.093417
rain_duration        2.059975  1        1.435261
factor(day_night)    1.833637  1        1.354119
factor(time_band)    1.113687  2        1.027285
factor(season)       2.762347  3        1.184531
factor(weather_main) 2.912232 10        1.054900
# to obtain residuals
res.fullmodel1 <- residuals(workModelSevilleFull) 
# to obtain standardized residuals
std.res.fullmodel1 <- rstandard(workModelSevilleFull) 
# to obtain fitted/predicted values
pred.fullmodel1 <- fitted.values(workModelSevilleFull) 
par(mfrow=c(1,1))
qqnorm(y = std.res.fullmodel1, main = " Normal Q-Q Plot ",
       xlab = "Theoretical Quantiles", ylab = "Sample Quantiles")
qqline(y = std.res.fullmodel1)

#residual plot
resplotdata1 <- data.frame(std.res.fullmodel1, pred.fullmodel1)
resbf1 <- lm(std.res.fullmodel1 ~ pred.fullmodel1, data = resplotdata1)
plot(x = pred.fullmodel1, y = std.res.fullmodel1, ylab = "Standardized Residuals", xlab = "Predicted Values", main = "Residuals Plot", col = ifelse(std.res.fullmodel1 < -3,"red",ifelse(std.res.fullmodel1 > 3,"red","black")))
abline(h = 0, col="blue", lty=1)
abline(resbf1, col="red", lty=3)
abline(h = 3, col="green", lty=3)
abline(h=-3, col="green", lty=3)
legend("bottomleft", legend=c("Best fit line of standardized residuals", "Horizontal line y = 0.0", "Horizontal line, y = +/- 3"), fill = c("red","blue","green"), cex = 0.5)

summary(workModelSevilleFull)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    wind_speed + rain_duration + factor(day_night) + factor(time_band) + 
    factor(season) + factor(weather_main), data = workSeville)

Residuals:
    Min      1Q  Median      3Q     Max 
-549.95  -30.21    4.87   33.75  478.29 

Coefficients:
                          Estimate  Std. Error t value             Pr(>|t|)    
(Intercept)            -932.840821   68.808830 -13.557 < 0.0000000000000002 ***
E_1                       0.890426    0.002422 367.692 < 0.0000000000000002 ***
E_25                      0.111392    0.002398  46.461 < 0.0000000000000002 ***
temp                      1.515006    0.077855  19.459 < 0.0000000000000002 ***
humidity                 -0.355891    0.024362 -14.608 < 0.0000000000000002 ***
pressure                  0.483766    0.060212   8.034 0.000000000000000977 ***
wind_speed                2.987490    0.190422  15.689 < 0.0000000000000002 ***
rain_duration             3.732190    2.169563   1.720             0.085398 .  
factor(day_night)2       26.545740    0.882968  30.064 < 0.0000000000000002 ***
factor(time_band)2       -2.706043    4.383971  -0.617             0.537069    
factor(time_band)3       25.388655    3.414551   7.435 0.000000000000107216 ***
factor(season)2         -17.569908    1.092649 -16.080 < 0.0000000000000002 ***
factor(season)3          -5.339033    0.962640  -5.546 0.000000029451488123 ***
factor(season)4           6.990646    1.115924   6.264 0.000000000379592466 ***
factor(weather_main)2     2.242919    0.892504   2.513             0.011974 *  
factor(weather_main)3   -12.318180    4.275098  -2.881             0.003962 ** 
factor(weather_main)4   -14.457822    3.478341  -4.157 0.000032408148010203 ***
factor(weather_main)5   -15.316699    3.093864  -4.951 0.000000743884166897 ***
factor(weather_main)6   -35.864988    3.428151 -10.462 < 0.0000000000000002 ***
factor(weather_main)7    -7.805466    2.448502  -3.188             0.001435 ** 
factor(weather_main)8     4.944984    2.054759   2.407             0.016108 *  
factor(weather_main)9    41.459354   11.394392   3.639             0.000275 ***
factor(weather_main)11   26.081350   54.527770   0.478             0.632431    
factor(weather_main)12    7.346205    5.619242   1.307             0.191111    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 54.52 on 27976 degrees of freedom
Multiple R-squared:   0.92, Adjusted R-squared:  0.9199 
F-statistic: 1.399e+04 on 23 and 27976 DF,  p-value: < 0.00000000000000022
df<- workModelSevilleFull %>% 
  tidy()
#Get the variables which there p-value larger than 0.05 
df%>%
  filter(df$p.value>0.05)
workModelSevillePvalue <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workSeville)
summary(workModelSevillePvalue)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    wind_speed + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main), data = workSeville)

Residuals:
    Min      1Q  Median      3Q     Max 
-549.95  -30.16    4.88   33.74  478.28 

Coefficients:
                          Estimate  Std. Error t value             Pr(>|t|)    
(Intercept)            -926.766568   68.720577 -13.486 < 0.0000000000000002 ***
E_1                       0.890376    0.002422 367.685 < 0.0000000000000002 ***
E_25                      0.111475    0.002397  46.503 < 0.0000000000000002 ***
temp                      1.513449    0.077853  19.440 < 0.0000000000000002 ***
humidity                 -0.354561    0.024351 -14.561 < 0.0000000000000002 ***
pressure                  0.478155    0.060126   7.953  0.00000000000000189 ***
wind_speed                2.994190    0.190388  15.727 < 0.0000000000000002 ***
factor(day_night)2       26.504162    0.882668  30.027 < 0.0000000000000002 ***
factor(time_band)2       -2.815274    4.383665  -0.642             0.520736    
factor(time_band)3       25.368138    3.414650   7.429  0.00000000000011237 ***
factor(season)2         -17.582847    1.092661 -16.092 < 0.0000000000000002 ***
factor(season)3          -5.340872    0.962673  -5.548  0.00000002916363891 ***
factor(season)4           6.970419    1.115901   6.246  0.00000000042592646 ***
factor(weather_main)2     2.210683    0.892339   2.477             0.013240 *  
factor(weather_main)3   -11.990918    4.271012  -2.808             0.004996 ** 
factor(weather_main)4   -14.479694    3.478440  -4.163  0.00003154496399759 ***
factor(weather_main)5   -15.332309    3.093959  -4.956  0.00000072541913847 ***
factor(weather_main)6   -35.873529    3.428268 -10.464 < 0.0000000000000002 ***
factor(weather_main)7    -7.783363    2.448554  -3.179             0.001481 ** 
factor(weather_main)8     7.245024    1.560299   4.643  0.00000344352885666 ***
factor(weather_main)9    41.439988   11.394785   3.637             0.000277 ***
factor(weather_main)11   26.078549   54.529679   0.478             0.632480    
factor(weather_main)12    8.050709    5.604495   1.436             0.150879    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 54.52 on 27977 degrees of freedom
Multiple R-squared:   0.92, Adjusted R-squared:  0.9199 
F-statistic: 1.462e+04 on 22 and 27977 DF,  p-value: < 0.00000000000000022
  • p-value approach
anova(workModelSevillePvalue, workModelSevilleFull)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df      RSS Df Sum of Sq      F Pr(>F)  
1  27977 83169196                             
2  27976 83160400  1    8796.6 2.9593 0.0854 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
  • Best subset selection method
bestfits <- regsubsets(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workSeville, nbest = 1)
plot(bestfits, scale="adjr2")

workModelSevilleBestfit<-lm(energy_demand ~ E_1 + E_25 + temp + humidity + wind_speed + factor(day_night) + factor(season) , data = workSeville)
summary(workModelSevilleBestfit)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + wind_speed + 
    factor(day_night) + factor(season), data = workSeville)

Residuals:
    Min      1Q  Median      3Q     Max 
-548.84  -30.38    5.07   33.74  477.84 

Coefficients:
                      Estimate  Std. Error t value             Pr(>|t|)    
(Intercept)        -393.271898   23.487782 -16.744 < 0.0000000000000002 ***
E_1                   0.890138    0.002426 366.882 < 0.0000000000000002 ***
E_25                  0.113507    0.002400  47.294 < 0.0000000000000002 ***
temp                  1.352323    0.076360  17.710 < 0.0000000000000002 ***
humidity             -0.377995    0.022132 -17.079 < 0.0000000000000002 ***
wind_speed            3.012031    0.182150  16.536 < 0.0000000000000002 ***
factor(day_night)2   25.972373    0.870245  29.845 < 0.0000000000000002 ***
factor(season)2     -18.083837    1.077474 -16.784 < 0.0000000000000002 ***
factor(season)3      -4.877645    0.960503  -5.078          0.000000383 ***
factor(season)4       9.565773    1.029338   9.293 < 0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 54.85 on 27990 degrees of freedom
Multiple R-squared:  0.919, Adjusted R-squared:  0.9189 
F-statistic: 3.527e+04 on 9 and 27990 DF,  p-value: < 0.00000000000000022
anova(workModelSevilleBestfit, workModelSevillePvalue)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + wind_speed + factor(day_night) + 
    factor(season)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df      RSS Df Sum of Sq      F                Pr(>F)    
1  27990 84221007                                              
2  27977 83169196 13   1051811 27.216 < 0.00000000000000022 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
  • AIC with forward selection approach test method
workNullModel<- lm(energy_demand ~ 1, data = workSeville)
step.working <- stepAIC(workNullModel, scope = list(lower = workNullModel,
upper = workModelSevilleFull), direction = "forward", trace=FALSE)
summary(step.working)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + humidity + factor(day_night) + 
    wind_speed + factor(season) + temp + factor(weather_main) + 
    pressure + factor(time_band) + rain_duration, data = workSeville)

Residuals:
    Min      1Q  Median      3Q     Max 
-549.95  -30.21    4.87   33.75  478.29 

Coefficients:
                          Estimate  Std. Error t value             Pr(>|t|)    
(Intercept)            -932.840821   68.808830 -13.557 < 0.0000000000000002 ***
E_1                       0.890426    0.002422 367.692 < 0.0000000000000002 ***
E_25                      0.111392    0.002398  46.461 < 0.0000000000000002 ***
humidity                 -0.355891    0.024362 -14.608 < 0.0000000000000002 ***
factor(day_night)2       26.545740    0.882968  30.064 < 0.0000000000000002 ***
wind_speed                2.987490    0.190422  15.689 < 0.0000000000000002 ***
factor(season)2         -17.569908    1.092649 -16.080 < 0.0000000000000002 ***
factor(season)3          -5.339033    0.962640  -5.546 0.000000029451488123 ***
factor(season)4           6.990646    1.115924   6.264 0.000000000379592466 ***
temp                      1.515006    0.077855  19.459 < 0.0000000000000002 ***
factor(weather_main)2     2.242919    0.892504   2.513             0.011974 *  
factor(weather_main)3   -12.318180    4.275098  -2.881             0.003962 ** 
factor(weather_main)4   -14.457822    3.478341  -4.157 0.000032408148010197 ***
factor(weather_main)5   -15.316699    3.093864  -4.951 0.000000743884166896 ***
factor(weather_main)6   -35.864988    3.428151 -10.462 < 0.0000000000000002 ***
factor(weather_main)7    -7.805466    2.448502  -3.188             0.001435 ** 
factor(weather_main)8     4.944984    2.054759   2.407             0.016108 *  
factor(weather_main)9    41.459354   11.394392   3.639             0.000275 ***
factor(weather_main)11   26.081350   54.527770   0.478             0.632431    
factor(weather_main)12    7.346205    5.619242   1.307             0.191111    
pressure                  0.483766    0.060212   8.034 0.000000000000000977 ***
factor(time_band)2       -2.706043    4.383971  -0.617             0.537069    
factor(time_band)3       25.388655    3.414551   7.435 0.000000000000107216 ***
rain_duration             3.732190    2.169563   1.720             0.085398 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 54.52 on 27976 degrees of freedom
Multiple R-squared:   0.92, Adjusted R-squared:  0.9199 
F-statistic: 1.399e+04 on 23 and 27976 DF,  p-value: < 0.00000000000000022
  • Cross Validation test method
# Set seed for reproducibility in CV
set.seed(123)
# Set up repeated k-fold cross-validation, with k=10
train.control <- trainControl(method = "cv", number = 10)
# Train the model
step.cv.work <- train(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workSeville, method = "leapBackward",
tuneGrid = data.frame(nvmax = 1:11),
trControl = train.control)
Reordering variables and trying again:
step.cv.work$results
summary(step.cv.work$finalModel)
Subset selection object
23 Variables  (and intercept)
                       Forced in Forced out
E_1                        FALSE      FALSE
E_25                       FALSE      FALSE
temp                       FALSE      FALSE
humidity                   FALSE      FALSE
pressure                   FALSE      FALSE
wind_speed                 FALSE      FALSE
rain_duration              FALSE      FALSE
factor(day_night)2         FALSE      FALSE
factor(time_band)2         FALSE      FALSE
factor(time_band)3         FALSE      FALSE
factor(season)2            FALSE      FALSE
factor(season)3            FALSE      FALSE
factor(season)4            FALSE      FALSE
factor(weather_main)2      FALSE      FALSE
factor(weather_main)3      FALSE      FALSE
factor(weather_main)4      FALSE      FALSE
factor(weather_main)5      FALSE      FALSE
factor(weather_main)6      FALSE      FALSE
factor(weather_main)7      FALSE      FALSE
factor(weather_main)8      FALSE      FALSE
factor(weather_main)9      FALSE      FALSE
factor(weather_main)11     FALSE      FALSE
factor(weather_main)12     FALSE      FALSE
1 subsets of each size up to 11
Selection Algorithm: backward
          E_1 E_25 temp humidity pressure wind_speed rain_duration factor(day_night)2 factor(time_band)2 factor(time_band)3 factor(season)2
1  ( 1 )  "*" " "  " "  " "      " "      " "        " "           " "                " "                " "                " "            
2  ( 1 )  "*" "*"  " "  " "      " "      " "        " "           " "                " "                " "                " "            
3  ( 1 )  "*" "*"  " "  "*"      " "      " "        " "           " "                " "                " "                " "            
4  ( 1 )  "*" "*"  " "  "*"      " "      " "        " "           "*"                " "                " "                " "            
5  ( 1 )  "*" "*"  " "  "*"      " "      "*"        " "           "*"                " "                " "                " "            
6  ( 1 )  "*" "*"  " "  "*"      " "      "*"        " "           "*"                " "                " "                "*"            
7  ( 1 )  "*" "*"  "*"  "*"      " "      "*"        " "           "*"                " "                " "                "*"            
8  ( 1 )  "*" "*"  "*"  "*"      " "      "*"        " "           "*"                " "                " "                "*"            
9  ( 1 )  "*" "*"  "*"  "*"      " "      "*"        " "           "*"                " "                " "                "*"            
10  ( 1 ) "*" "*"  "*"  "*"      " "      "*"        " "           "*"                " "                "*"                "*"            
11  ( 1 ) "*" "*"  "*"  "*"      "*"      "*"        " "           "*"                " "                "*"                "*"            
          factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)4 factor(weather_main)5 factor(weather_main)6
1  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
2  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
3  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
4  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
5  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
6  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
7  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
8  ( 1 )  " "             "*"             " "                   " "                   " "                   " "                   " "                  
9  ( 1 )  " "             "*"             " "                   " "                   " "                   " "                   "*"                  
10  ( 1 ) " "             "*"             " "                   " "                   " "                   " "                   "*"                  
11  ( 1 ) " "             "*"             " "                   " "                   " "                   " "                   "*"                  
          factor(weather_main)7 factor(weather_main)8 factor(weather_main)9 factor(weather_main)11 factor(weather_main)12
1  ( 1 )  " "                   " "                   " "                   " "                    " "                   
2  ( 1 )  " "                   " "                   " "                   " "                    " "                   
3  ( 1 )  " "                   " "                   " "                   " "                    " "                   
4  ( 1 )  " "                   " "                   " "                   " "                    " "                   
5  ( 1 )  " "                   " "                   " "                   " "                    " "                   
6  ( 1 )  " "                   " "                   " "                   " "                    " "                   
7  ( 1 )  " "                   " "                   " "                   " "                    " "                   
8  ( 1 )  " "                   " "                   " "                   " "                    " "                   
9  ( 1 )  " "                   " "                   " "                   " "                    " "                   
10  ( 1 ) " "                   " "                   " "                   " "                    " "                   
11  ( 1 ) " "                   " "                   " "                   " "                    " "                   

Reduced model same as P-value approach

  • OLS method
#ModelOLS <- ols_step_all_possible(workModelSevilleFull)
#cat("\n")
#plot(x = ModelOLS$n, y = ModelOLS$adjr)
#plot(x = ModelOLS$n, y = ModelOLS$aic)
#OLSsummary <- ModelOLS %>% group_by(n) %>%  filter(adjr == max(adjr))

Same reduced model as P-Value approach (thus commenting out given processing time)

Select the final model

Model adjusted R-square from above methods (to four significant digits)

  1. Full: 0.9199

  2. P-value: 0.9199

  3. Bestfits: 0.9189

  4. AIC Method: 0.9199

  5. Cross-validation: 0.9199

Final model is the workModelSevillePvalue with highest Adjusted R-square and fewest predictors.

Method <- c('Full','P-value', 'BestFits', 'AIC Forward', 'Cross Validation')
Variables <- c(12,11,8,12,11)
Coefficients <- c(24,23,10,24,23)
Adjusted_R2 <- c(0.9199264,0.9199208,0.9189457,0.9199264,0.9199208)
SevilleTable<-data.frame(Method,Variables,Coefficients,Adjusted_R2)

Build the Final Model

workFinal <- workModelSevillePvalue

Check the linearity, heteroscedasticity, outlier and normal assumption

par(mfrow=c(2,2))
#Residual Plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
#norm test
std.res <- rstandard(workFinal)
ad.test(std.res)

    Anderson-Darling normality test

data:  std.res
A = 131.25, p-value < 0.00000000000000022
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
#leverage 
lev<-hatvalues(workFinal)
cutlev = (2*length(coefficients(workFinal)))/nrow(workSeville)
# Count and assess the number of leverage values > cut-off
potoutlier <- sum(lev > cutlev, na.rm=TRUE)
totcount = length(fitted(workFinal))
print(paste("The number of leverage points that are potential outliers is", potoutlier,".  This is", round(100*potoutlier/totcount,2),"% of the total number of predicted values, (", totcount,") thus not material."))
[1] "The number of leverage points that are potential outliers is 2134 .  This is 7.62 % of the total number of predicted values, ( 28000 ) thus not material."
#barplot(lev, ylim = c(0, 2*cutlev))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for the working set")
abline(h = cutlev, col = "red", lty=2)

#cook distance
cookdist<-cooks.distance(workFinal)
#barplot(cookdist, ylim=c(0,1.01), main = "Cook's Distance plot")
#abline(h = 1, col = "red")
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.03,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")

# Opening the graphical device
# Customizing the output
pdf("SevilleAssumptionCheck1.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
# par(mfrow=c(2,2))
#residuals scatter plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#normality plot
pdf("SevilleAssumptionCheck2.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
grid(10,10)
dev.off()
null device 
          1 
#leverage plot
pdf("SevilleAssumptionCheck3.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for Barcelona working set")
abline(h = cutlev, col = "red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#Cook's distance plot
pdf("SevilleAssumptionCheck4.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.005,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")
dev.off()
null device 
          1 

Use the estimated model to predict the values in the evaluation set

newdata <- evalSeville[ c(-2,-13)]
predict.eval <- predict(workFinal, newdata)
plot(x = evalSeville$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Seville")
abline(a=0, b=1, col="blue")
grid(10,10)

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("SevilleEvalActComp.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,1))
plot(x = evalSeville$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Seville", cex.lab = 0.75)
abline(a=0, b=1, col="blue")
grid(10,10)

# Closing the graphical device
dev.off()
null device 
          1 

Compute the RMSE for the predictions

rootmse <- rmse(evalSeville$energy_demand, predict.eval)
rootmse <- rmse(evaldata$energy_demand, predict.eval)
print(paste("The root mean square error of the predicted vs actual",nrow(evaldata),"hourly energy_data points for Seville over the 2015-2018 timespan is an energy demand difference of",round(rootmse,2),"MWh."))
[1] "The root mean square error of the predicted vs actual 7001 hourly energy_data points for Seville over the 2015-2018 timespan is an energy demand difference of 54.39 MWh."
cat("\n")
demandcompdf <- data.frame("Actual (MWh)" = evalSeville$energy_demand,"Predicted (MWh)" = predict.eval)
head(demandcompdf,4L)
cat("\n")
print(paste("For example, the first actual demand value is",round(evaldata$energy_demand[1],2),"MWh and the corresponding predicted value is",round(predict.eval[1],2),"MWh. The percent error between this predicted value relative to the actual value is",round((100*(predict.eval[1] - evaldata$energy_demand[1])/evaldata$energy_demand[1]),2),"%."))
[1] "For example, the first actual demand value is 1025.77 MWh and the corresponding predicted value is 962.18 MWh. The percent error between this predicted value relative to the actual value is -6.2 %."
cat("\n")
diffpct <- c((abs(evaldata$energy_demand - predict.eval))/evaldata$energy_demand)
print(paste("The mean difference between the",nrow(evaldata),"evaluation data set actual and model predicted values is",round(100*mean(diffpct),2),"%."))
[1] "The mean difference between the 7001 evaluation data set actual and model predicted values is 3.49 %."

Compute and analyze Hat Matrix for Evaluation data set

Obtain X matrix from the model

Predictors <-rownames(summary(workFinal)$coefficients[,])
workpredictors <- as.data.frame(Predictors)
X <- model.matrix(workFinal)
class(X)
[1] "matrix" "array" 
cat("\n")
dim(X)
[1] 28000    23
cat("\n")
head(X, 4L)
  (Intercept)       E_1      E_25    temp humidity pressure wind_speed factor(day_night)2 factor(time_band)2 factor(time_band)3 factor(season)2
1           1 1025.7701 1138.8564 273.375       75     1039          1                  1                  0                  0               0
2           1  895.5189  976.6314 274.086       71     1039          3                  1                  0                  0               0
3           1  852.5226  948.2757 274.086       71     1039          3                  1                  0                  0               0
4           1  837.4192  929.5963 274.086       71     1039          3                  1                  0                  0               0
  factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)4 factor(weather_main)5 factor(weather_main)6
1               0               1                     0                     0                     0                     0                     0
2               0               1                     0                     0                     0                     0                     0
3               0               1                     0                     0                     0                     0                     0
4               0               1                     0                     0                     0                     0                     0
  factor(weather_main)7 factor(weather_main)8 factor(weather_main)9 factor(weather_main)11 factor(weather_main)12
1                     0                     0                     0                      0                      0
2                     0                     0                     0                      0                      0
3                     0                     0                     0                      0                      0
4                     0                     0                     0                      0                      0

Create x_new matrix using the validation data. Note that the purpose of running the evaluation regression here is to extract all of the correct predictor coefficients.

evalFinal <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
                  wind_speed + factor(day_night) + factor(time_band) + 
                  factor(season) + factor(weather_main), data = evalSeville)
Predictors <-rownames(summary(evalFinal)$coefficients[,])
evalpredictors <- as.data.frame(Predictors)
x_new1 <- model.matrix(evalFinal)
cat("\n")
dim(x_new1)
[1] 7001   22
cat("\n")
#Check if there's a predictor difference between the work and eval matrices
workevalcompare <-anti_join(workpredictors,evalpredictors)
Joining, by = "Predictors"
cat("\n")
#If workevalcompare is 'No data available in table', run the next line of code and skip to line 1580
#x_new <- x_new1
#Otherwise when workevalcompare is not 'No data available in table', run the following lines of code after identifying which column is missing between X and x_new1 (for Seville, it was found that "factor(weather_main)11" appears in workSeville but not in evalSeville).
x_new_ <-as.data.frame(cbind(x_new1,rep(0,nrow(x_new1))))
dummy_xnew<- x_new_ %>% relocate(V23,.before = 22) %>% rename("factor(weather_main)11" = V23)
x_new <- as.matrix(dummy_xnew)  
cat("\n")
class(x_new)
[1] "matrix" "array" 
cat("\n")
dim(x_new)
[1] 7001   23
cat("\n")
head(x_new, 4L)
  (Intercept)      E_1      E_25    temp humidity pressure wind_speed factor(day_night)2 factor(time_band)2 factor(time_band)3 factor(season)2
1           1 956.4375 1049.0353 273.375       75     1039          1                  1                  0                  0               0
2           1 845.3706  968.6801 274.592       81     1039          4                  1                  0                  0               0
3           1 868.2150  997.0358 275.651       73     1041          2                  0                  1                  0               0
4           1 972.5926 1176.8463 286.994       53     1036          3                  0                  1                  0               0
  factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)4 factor(weather_main)5 factor(weather_main)6
1               0               1                     0                     0                     0                     0                     0
2               0               1                     0                     0                     0                     0                     0
3               0               1                     0                     0                     0                     0                     0
4               0               1                     0                     0                     0                     0                     0
  factor(weather_main)7 factor(weather_main)8 factor(weather_main)9 factor(weather_main)11 factor(weather_main)12
1                     0                     0                     0                      0                      0
2                     0                     0                     0                      0                      0
3                     0                     0                     0                      0                      0
4                     0                     0                     0                      0                      0

Compute the hat values for the validation data

h_new_mid <- solve(t(X)%*%X)
dim(h_new_mid)
[1] 23 23
h_new <- x_new%*%h_new_mid%*%t(x_new)
dim(h_new)
[1] 7001 7001

Plot the hatvalues for the data in the evaluation set

#cutlev2 = (2*length(coefficients(evalFinal)))/nrow(evalSeville)
# Count and assess the number of leverage values > cut-off
potoutlier2 <- sum(diag(h_new) > cutlev, na.rm=TRUE)
totcount2 = nrow(evalSeville)
plot(diag(h_new), type = "h", ylab = "Leverage for the validation data set", ylim = c(0,2*cutlev), main = "Seville")
abline(h=cutlev, col="red", lty=2)

print(paste("The number of leverage points that are potential outliers is", potoutlier2,".  This is", round(100*potoutlier2/totcount2,2),"% of the total number of predicted values, (", totcount2,") thus not material."))
[1] "The number of leverage points that are potential outliers is 532 .  This is 7.6 % of the total number of predicted values, ( 7001 ) thus not material."

Side-by-side comparision

par(mfrow=c(1,2))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for the working data set", main = "Seville")
abline(h = cutlev, col = "red", lty=2)
plot(diag(h_new), type = "h", ylim = c(0,2*cutlev), ylab = "Leverage for the evaluation data set", main = "Seville")
abline(h=cutlev, col="red", lty=2)

print(paste("The leverage cutoff exceedences in the evaluation set is",round(100*potoutlier2/totcount2,2),"% of the total number of observed values (", totcount2,") and is comparable to that of the working set,", round(100*potoutlier/totcount,2),"% of the total number of predicted values (", totcount,")."))
[1] "The leverage cutoff exceedences in the evaluation set is 7.6 % of the total number of observed values ( 7001 ) and is comparable to that of the working set, 7.62 % of the total number of predicted values ( 28000 )."

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("SevilleLevComp.pdf",         # File name
    width = 14, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,2))
plot(lev, type="h", ylab = "Leverage", ylim = c(0,2*cutlev), yaxt = 'n', main = "Seville Working Data", res =600)
abline(h = cutlev, col = "red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

plot(diag(h_new), type = "h", ylab = "", ylim = c(0,2*cutlev), yaxt = 'n', main = "Seville Evaluation Data")
abline(h=cutlev, col="red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

# Closing the graphical device
dev.off()
null device 
          1 

Valencia Model

evaldata<-read.csv('data/evaluation_Valencia.csv')
workdata<-read.csv('data/working_Valencia.csv')
#remove index and time_ID columns from "eval" analysis dataframe
evalValencia<-evaldata[c(-1,-2)] 
#remove index and time_ID columns from "work" analysis dataframe
workValencia<-workdata[c(-1,-2)] 
head(workValencia, 4L)
# Correlation matrix for numeric predictors only (categorical predictors excluded)
workcor<-workValencia[c(-10:-13) ]
ggpairs(workcor)

cor(workValencia)
                       E_1          E_2         E_25        temp    humidity    pressure  wind_speed rain_duration snow_duration   day_night
E_1            1.000000000  0.951054128  0.666939844  0.19109473 -0.28730452  0.01627940  0.13872213  0.0268061475  -0.005782161 -0.54254634
E_2            0.951054128  1.000000000  0.571164357  0.15367732 -0.25124085  0.01529907  0.12787798  0.0220813226  -0.005757167 -0.53504092
E_25           0.666939844  0.571164357  1.000000000  0.21893597 -0.31975014  0.01584015  0.16516026  0.0095613923  -0.005214940 -0.51180659
temp           0.191094734  0.153677316  0.218935967  1.00000000 -0.40538712 -0.12401369  0.08321668 -0.0828673326  -0.017524492 -0.38474840
humidity      -0.287304516 -0.251240855 -0.319750141 -0.40538712  1.00000000  0.07482097 -0.38567524  0.1341640999  -0.011280937  0.44420938
pressure       0.016279401  0.015299073  0.015840152 -0.12401369  0.07482097  1.00000000 -0.12331451 -0.0731064209  -0.016919098  0.05327426
wind_speed     0.138722130  0.127877981  0.165160263  0.08321668 -0.38567524 -0.12331451  1.00000000  0.0235262566   0.011156495 -0.18975982
rain_duration  0.026806148  0.022081323  0.009561392 -0.08286733  0.13416410 -0.07310642  0.02352626  1.0000000000   0.009657072 -0.01079121
snow_duration -0.005782161 -0.005757167 -0.005214940 -0.01752449 -0.01128094 -0.01691910  0.01115650  0.0096570721   1.000000000 -0.01016594
day_night     -0.542546338 -0.535040922 -0.511806591 -0.38474840  0.44420938  0.05327426 -0.18975982 -0.0107912145  -0.010165940  1.00000000
time_band      0.110986153  0.091678654  0.113058687 -0.13846625 -0.01513187  0.02024851  0.13793701 -0.0001837309   0.085006810 -0.00297295
season         0.078852572  0.079163395  0.077294925 -0.33696550  0.15583643  0.26596837  0.04690817 -0.0457883003   0.014244862  0.15019176
weather_main   0.032252910  0.025255947  0.020597389 -0.06837167  0.23950590 -0.12315269  0.04802916  0.4778286676   0.035801103 -0.03744148
energy_demand  0.951146210  0.832911624  0.700323450  0.22238880 -0.31157854  0.01880238  0.14782748  0.0294762210  -0.006658955 -0.50985063
                  time_band      season weather_main energy_demand
E_1            0.1109861526  0.07885257   0.03225291   0.951146210
E_2            0.0916786545  0.07916339   0.02525595   0.832911624
E_25           0.1130586874  0.07729493   0.02059739   0.700323450
temp          -0.1384662509 -0.33696550  -0.06837167   0.222388801
humidity      -0.0151318666  0.15583643   0.23950590  -0.311578541
pressure       0.0202485123  0.26596837  -0.12315269   0.018802377
wind_speed     0.1379370082  0.04690817   0.04802916   0.147827481
rain_duration -0.0001837309 -0.04578830   0.47782867   0.029476221
snow_duration  0.0850068105  0.01424486   0.03580110  -0.006658955
day_night     -0.0029729496  0.15019176  -0.03744148  -0.509850631
time_band      1.0000000000  0.16430656   0.01155377   0.123586398
season         0.1643065599  1.00000000  -0.02378259   0.078241487
weather_main   0.0115537696 -0.02378259   1.00000000   0.037878776
energy_demand  0.1235863979  0.07824149   0.03787878   1.000000000
corrplot(cor(workcor), method="color", type="full", addCoef.col = "red", tl.col="black",number.cex = 0.75)

workModelValencia1 <- lm(energy_demand ~ E_1 + E_2 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workValencia)
vif(workModelValencia1)
                          GVIF Df GVIF^(1/(2*Df))
E_1                  14.171607  1        3.764520
E_2                  12.017128  1        3.466573
E_25                  2.156342  1        1.468449
temp                  3.403566  1        1.844876
humidity              1.994951  1        1.412427
pressure              1.139913  1        1.067667
wind_speed            1.331132  1        1.153747
rain_duration         1.456960  1        1.207046
snow_duration         1.011179  1        1.005574
factor(day_night)     1.965917  1        1.402112
factor(time_band)     1.111991  2        1.026893
factor(season)        3.270467  3        1.218339
factor(weather_main)  1.816528  8        1.038013
workModelValenciaFull <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workValencia)
vif(workModelValenciaFull)
                         GVIF Df GVIF^(1/(2*Df))
E_1                  2.039961  1        1.428272
E_25                 1.973528  1        1.404823
temp                 3.339423  1        1.827409
humidity             1.993134  1        1.411784
pressure             1.139889  1        1.067656
wind_speed           1.329233  1        1.152924
rain_duration        1.456598  1        1.206896
snow_duration        1.011170  1        1.005569
factor(day_night)    1.889044  1        1.374425
factor(time_band)    1.108226  2        1.026023
factor(season)       3.226785  3        1.215612
factor(weather_main) 1.813697  8        1.037911
# to obtain residuals
res.fullmodel1 <- residuals(workModelValenciaFull) 
# to obtain standardized residuals
std.res.fullmodel1 <- rstandard(workModelValenciaFull) 
# to obtain fitted/predicted values
pred.fullmodel1 <- fitted.values(workModelValenciaFull) 
par(mfrow=c(1,1))
qqnorm(y = std.res.fullmodel1, main = " Normal Q-Q Plot ",
       xlab = "Theoretical Quantiles", ylab = "Sample Quantiles")
qqline(y = std.res.fullmodel1)

#residual plot
resplotdata1 <- data.frame(std.res.fullmodel1, pred.fullmodel1)
resbf1 <- lm(std.res.fullmodel1 ~ pred.fullmodel1, data = resplotdata1)
plot(x = pred.fullmodel1, y = std.res.fullmodel1, ylab = "Standardized Residuals", xlab = "Predicted Values", main = "Residuals Plot", col = ifelse(std.res.fullmodel1 < -3,"red",ifelse(std.res.fullmodel1 > 3,"red","black")))
abline(h = 0, col="blue", lty=1)
abline(resbf1, col="red", lty=3)
abline(h = 3, col="green", lty=3)
abline(h=-3, col="green", lty=3)
legend("bottomleft", legend=c("Best fit line of standardized residuals", "Horizontal line y = 0.0", "Horizontal line, y = +/- 3"), fill = c("red","blue","green"), cex = 0.5)

summary(workModelValenciaFull)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    wind_speed + rain_duration + snow_duration + factor(day_night) + 
    factor(time_band) + factor(season) + factor(weather_main), 
    data = workValencia)

Residuals:
    Min      1Q  Median      3Q     Max 
-714.78  -41.44    6.33   45.09  647.09 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -1003.690971    56.667174 -17.712 < 0.0000000000000002 ***
E_1                        0.884855     0.002462 359.354 < 0.0000000000000002 ***
E_25                       0.118354     0.002424  48.819 < 0.0000000000000002 ***
temp                       3.036519     0.111534  27.225 < 0.0000000000000002 ***
humidity                  -0.325270     0.031569 -10.303 < 0.0000000000000002 ***
pressure                   0.123376     0.046657   2.644              0.00819 ** 
wind_speed                -0.852805     0.194388  -4.387  0.00001152758427349 ***
rain_duration              6.035686     1.965320   3.071              0.00213 ** 
snow_duration            -48.622588    51.418802  -0.946              0.34435    
factor(day_night)2        34.469671     1.211297  28.457 < 0.0000000000000002 ***
factor(time_band)2         2.754287     6.014925   0.458              0.64702    
factor(time_band)3        44.964025     4.505279   9.980 < 0.0000000000000002 ***
factor(season)2          -26.939800     1.654365 -16.284 < 0.0000000000000002 ***
factor(season)3          -10.371166     1.345340  -7.709  0.00000000000001311 ***
factor(season)4            9.712012     1.440856   6.740  0.00000000001610023 ***
factor(weather_main)2      4.982183     0.968803   5.143  0.00000027278240873 ***
factor(weather_main)3     26.571095    11.630332   2.285              0.02234 *  
factor(weather_main)5     -3.902488    12.443279  -0.314              0.75381    
factor(weather_main)6     28.645610    51.837893   0.553              0.58054    
factor(weather_main)7     15.461673     6.125109   2.524              0.01160 *  
factor(weather_main)8     20.305312     2.564494   7.918  0.00000000000000251 ***
factor(weather_main)9     83.961927    32.799314   2.560              0.01048 *  
factor(weather_main)12    12.036941     7.374031   1.632              0.10262    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 73.29 on 27682 degrees of freedom
Multiple R-squared:  0.9178,    Adjusted R-squared:  0.9177 
F-statistic: 1.404e+04 on 22 and 27682 DF,  p-value: < 0.00000000000000022
df<- workModelValenciaFull %>% 
  tidy()
#Get the variables which there p-value larger than 0.05 
df%>%
  filter(df$p.value>0.05)
workModelValenciaPvalue <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workValencia)
summary(workModelValenciaPvalue)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + pressure + 
    wind_speed + rain_duration + factor(day_night) + factor(time_band) + 
    factor(season) + factor(weather_main), data = workValencia)

Residuals:
    Min      1Q  Median      3Q     Max 
-714.80  -41.47    6.31   45.11  647.11 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -1004.925992    56.652013 -17.739 < 0.0000000000000002 ***
E_1                        0.884894     0.002462 359.422 < 0.0000000000000002 ***
E_25                       0.118386     0.002424  48.837 < 0.0000000000000002 ***
temp                       3.038226     0.111519  27.244 < 0.0000000000000002 ***
humidity                  -0.324448     0.031557 -10.281 < 0.0000000000000002 ***
pressure                   0.123945     0.046653   2.657              0.00789 ** 
wind_speed                -0.850685     0.194374  -4.377   0.0000121024974039 ***
rain_duration              6.061477     1.965127   3.085              0.00204 ** 
factor(day_night)2        34.492451     1.211055  28.481 < 0.0000000000000002 ***
factor(time_band)2         2.569012     6.011721   0.427              0.66914    
factor(time_band)3        44.633275     4.491673   9.937 < 0.0000000000000002 ***
factor(season)2          -26.966190     1.654127 -16.302 < 0.0000000000000002 ***
factor(season)3          -10.388206     1.345217  -7.722   0.0000000000000118 ***
factor(season)4            9.698814     1.440786   6.732   0.0000000000171069 ***
factor(weather_main)2      4.975234     0.968773   5.136   0.0000002831463489 ***
factor(weather_main)3     26.556133    11.630299   2.283              0.02242 *  
factor(weather_main)5     -3.927628    12.443227  -0.316              0.75228    
factor(weather_main)6     28.642884    51.837794   0.553              0.58058    
factor(weather_main)7     15.438547     6.125048   2.521              0.01172 *  
factor(weather_main)8     20.197124     2.561935   7.884   0.0000000000000033 ***
factor(weather_main)9     83.939146    32.799242   2.559              0.01050 *  
factor(weather_main)12    12.012127     7.373970   1.629              0.10333    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 73.29 on 27683 degrees of freedom
Multiple R-squared:  0.9178,    Adjusted R-squared:  0.9177 
F-statistic: 1.471e+04 on 21 and 27683 DF,  p-value: < 0.00000000000000022
  • p-value approach
anova(workModelValenciaPvalue, workModelValenciaFull)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + snow_duration + factor(day_night) + factor(time_band) + 
    factor(season) + factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1  27683 148683378                           
2  27682 148678576  1    4802.7 0.8942 0.3444
  • Best subset selection method
bestfits <- regsubsets(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workValencia, nbest = 1)
plot(bestfits, scale="adjr2")

workModelValenciaBestfit<-lm(energy_demand ~ E_1 + E_25 + temp + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workValencia)
summary(workModelValenciaBestfit)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + factor(day_night) + 
    factor(time_band) + factor(season) + factor(weather_main), 
    data = workValencia)

Residuals:
    Min      1Q  Median      3Q     Max 
-713.54  -41.68    6.32   45.48  645.50 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -1026.388603    29.108302 -35.261 < 0.0000000000000002 ***
E_1                        0.885725     0.002466 359.232 < 0.0000000000000002 ***
E_25                       0.120298     0.002420  49.719 < 0.0000000000000002 ***
temp                       3.465467     0.100419  34.510 < 0.0000000000000002 ***
factor(day_night)2        32.134863     1.187753  27.055 < 0.0000000000000002 ***
factor(time_band)2         0.989703     6.007040   0.165              0.86914    
factor(time_band)3        43.479302     4.476508   9.713 < 0.0000000000000002 ***
factor(season)2          -31.757105     1.552625 -20.454 < 0.0000000000000002 ***
factor(season)3          -13.493634     1.286982 -10.485 < 0.0000000000000002 ***
factor(season)4           10.088154     1.382228   7.298 0.000000000000298884 ***
factor(weather_main)2      2.443658     0.936693   2.609              0.00909 ** 
factor(weather_main)3     20.134668    11.638553   1.730              0.08364 .  
factor(weather_main)5    -11.784953    12.439785  -0.947              0.34346    
factor(weather_main)6     26.107025    51.946095   0.503              0.61526    
factor(weather_main)7      8.294013     6.087548   1.362              0.17307    
factor(weather_main)8     17.024099     2.091205   8.141 0.000000000000000409 ***
factor(weather_main)9     84.584591    32.867921   2.573              0.01007 *  
factor(weather_main)12     4.984582     7.346590   0.678              0.49747    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 73.44 on 27687 degrees of freedom
Multiple R-squared:  0.9174,    Adjusted R-squared:  0.9174 
F-statistic: 1.809e+04 on 17 and 27687 DF,  p-value: < 0.00000000000000022
anova(workModelValenciaBestfit, workModelValenciaPvalue)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + factor(day_night) + factor(time_band) + 
    factor(season) + factor(weather_main)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F                Pr(>F)    
1  27687 149330620                                              
2  27683 148683378  4    647242 30.127 < 0.00000000000000022 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
  • AIC with forward selection approach test method
workNullModel<- lm(energy_demand ~ 1, data = workValencia)
step.working <- stepAIC(workNullModel, scope = list(lower = workNullModel,
upper = workModelValenciaFull), direction = "forward", trace=FALSE)
summary(step.working)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + factor(day_night) + 
    factor(season) + factor(time_band) + factor(weather_main) + 
    humidity + wind_speed + rain_duration + pressure, data = workValencia)

Residuals:
    Min      1Q  Median      3Q     Max 
-714.80  -41.47    6.31   45.11  647.11 

Coefficients:
                           Estimate   Std. Error t value             Pr(>|t|)    
(Intercept)            -1004.925992    56.652013 -17.739 < 0.0000000000000002 ***
E_1                        0.884894     0.002462 359.422 < 0.0000000000000002 ***
E_25                       0.118386     0.002424  48.837 < 0.0000000000000002 ***
temp                       3.038226     0.111519  27.244 < 0.0000000000000002 ***
factor(day_night)2        34.492451     1.211055  28.481 < 0.0000000000000002 ***
factor(season)2          -26.966190     1.654127 -16.302 < 0.0000000000000002 ***
factor(season)3          -10.388206     1.345217  -7.722   0.0000000000000118 ***
factor(season)4            9.698814     1.440786   6.732   0.0000000000171069 ***
factor(time_band)2         2.569012     6.011721   0.427              0.66914    
factor(time_band)3        44.633275     4.491673   9.937 < 0.0000000000000002 ***
factor(weather_main)2      4.975234     0.968773   5.136   0.0000002831463489 ***
factor(weather_main)3     26.556133    11.630299   2.283              0.02242 *  
factor(weather_main)5     -3.927628    12.443227  -0.316              0.75228    
factor(weather_main)6     28.642884    51.837794   0.553              0.58058    
factor(weather_main)7     15.438547     6.125048   2.521              0.01172 *  
factor(weather_main)8     20.197124     2.561935   7.884   0.0000000000000033 ***
factor(weather_main)9     83.939146    32.799242   2.559              0.01050 *  
factor(weather_main)12    12.012127     7.373970   1.629              0.10333    
humidity                  -0.324448     0.031557 -10.281 < 0.0000000000000002 ***
wind_speed                -0.850685     0.194374  -4.377   0.0000121024974039 ***
rain_duration              6.061477     1.965127   3.085              0.00204 ** 
pressure                   0.123945     0.046653   2.657              0.00789 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 73.29 on 27683 degrees of freedom
Multiple R-squared:  0.9178,    Adjusted R-squared:  0.9177 
F-statistic: 1.471e+04 on 21 and 27683 DF,  p-value: < 0.00000000000000022
  • Cross Validation test method
# Set seed for reproducibility in CV
set.seed(123)
# Set up repeated k-fold cross-validation, with k=10
train.control <- trainControl(method = "cv", number = 10)
# Train the model
step.cv.work <- train(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + snow_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workValencia, method = "leapBackward",
tuneGrid = data.frame(nvmax = 1:12),
trControl = train.control)
step.cv.work$results
summary(step.cv.work$finalModel)
Subset selection object
22 Variables  (and intercept)
                       Forced in Forced out
E_1                        FALSE      FALSE
E_25                       FALSE      FALSE
temp                       FALSE      FALSE
humidity                   FALSE      FALSE
pressure                   FALSE      FALSE
wind_speed                 FALSE      FALSE
rain_duration              FALSE      FALSE
snow_duration              FALSE      FALSE
factor(day_night)2         FALSE      FALSE
factor(time_band)2         FALSE      FALSE
factor(time_band)3         FALSE      FALSE
factor(season)2            FALSE      FALSE
factor(season)3            FALSE      FALSE
factor(season)4            FALSE      FALSE
factor(weather_main)2      FALSE      FALSE
factor(weather_main)3      FALSE      FALSE
factor(weather_main)5      FALSE      FALSE
factor(weather_main)6      FALSE      FALSE
factor(weather_main)7      FALSE      FALSE
factor(weather_main)8      FALSE      FALSE
factor(weather_main)9      FALSE      FALSE
factor(weather_main)12     FALSE      FALSE
1 subsets of each size up to 12
Selection Algorithm: backward
          E_1 E_25 temp humidity pressure wind_speed rain_duration snow_duration factor(day_night)2 factor(time_band)2 factor(time_band)3 factor(season)2
1  ( 1 )  "*" " "  " "  " "      " "      " "        " "           " "           " "                " "                " "                " "            
2  ( 1 )  "*" "*"  " "  " "      " "      " "        " "           " "           " "                " "                " "                " "            
3  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           " "                " "                " "                " "            
4  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                " "                " "            
5  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                " "                "*"            
6  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                " "                "*"            
7  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                "*"                "*"            
8  ( 1 )  "*" "*"  "*"  " "      " "      " "        " "           " "           "*"                " "                "*"                "*"            
9  ( 1 )  "*" "*"  "*"  "*"      " "      " "        " "           " "           "*"                " "                "*"                "*"            
10  ( 1 ) "*" "*"  "*"  "*"      " "      " "        " "           " "           "*"                " "                "*"                "*"            
11  ( 1 ) "*" "*"  "*"  "*"      " "      " "        " "           " "           "*"                " "                "*"                "*"            
12  ( 1 ) "*" "*"  "*"  "*"      " "      "*"        " "           " "           "*"                " "                "*"                "*"            
          factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)5 factor(weather_main)6 factor(weather_main)7
1  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
2  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
3  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
4  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
5  ( 1 )  " "             " "             " "                   " "                   " "                   " "                   " "                  
6  ( 1 )  "*"             " "             " "                   " "                   " "                   " "                   " "                  
7  ( 1 )  "*"             " "             " "                   " "                   " "                   " "                   " "                  
8  ( 1 )  "*"             " "             " "                   " "                   " "                   " "                   " "                  
9  ( 1 )  "*"             " "             " "                   " "                   " "                   " "                   " "                  
10  ( 1 ) "*"             "*"             " "                   " "                   " "                   " "                   " "                  
11  ( 1 ) "*"             "*"             "*"                   " "                   " "                   " "                   " "                  
12  ( 1 ) "*"             "*"             "*"                   " "                   " "                   " "                   " "                  
          factor(weather_main)8 factor(weather_main)9 factor(weather_main)12
1  ( 1 )  " "                   " "                   " "                   
2  ( 1 )  " "                   " "                   " "                   
3  ( 1 )  " "                   " "                   " "                   
4  ( 1 )  " "                   " "                   " "                   
5  ( 1 )  " "                   " "                   " "                   
6  ( 1 )  " "                   " "                   " "                   
7  ( 1 )  " "                   " "                   " "                   
8  ( 1 )  "*"                   " "                   " "                   
9  ( 1 )  "*"                   " "                   " "                   
10  ( 1 ) "*"                   " "                   " "                   
11  ( 1 ) "*"                   " "                   " "                   
12  ( 1 ) "*"                   " "                   " "                   
workModelValenciaCross <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = workValencia)
anova( workModelValenciaCross, workModelValenciaPvalue)
Analysis of Variance Table

Model 1: energy_demand ~ E_1 + E_25 + temp + humidity + factor(day_night) + 
    factor(time_band) + factor(season) + factor(weather_main)
Model 2: energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + 
    rain_duration + factor(day_night) + factor(time_band) + factor(season) + 
    factor(weather_main)
  Res.Df       RSS Df Sum of Sq      F        Pr(>F)    
1  27686 148890185                                      
2  27683 148683378  3    206807 12.835 0.00000002238 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
summary(workModelValenciaCross)

Call:
lm(formula = energy_demand ~ E_1 + E_25 + temp + humidity + factor(day_night) + 
    factor(time_band) + factor(season) + factor(weather_main), 
    data = workValencia)

Residuals:
    Min      1Q  Median      3Q     Max 
-711.96  -41.22    6.24   45.23  645.94 

Coefficients:
                          Estimate  Std. Error t value             Pr(>|t|)    
(Intercept)            -881.560408   33.180368 -26.569 < 0.0000000000000002 ***
E_1                       0.885344    0.002462 359.549 < 0.0000000000000002 ***
E_25                      0.118381    0.002425  48.811 < 0.0000000000000002 ***
temp                      3.025870    0.111419  27.158 < 0.0000000000000002 ***
humidity                 -0.269465    0.029776  -9.050 < 0.0000000000000002 ***
factor(day_night)2       34.378128    1.211648  28.373 < 0.0000000000000002 ***
factor(time_band)2        0.123778    5.999046   0.021               0.9835    
factor(time_band)3       42.172498    4.472314   9.430 < 0.0000000000000002 ***
factor(season)2         -26.574433    1.652752 -16.079 < 0.0000000000000002 ***
factor(season)3         -10.019958    1.341205  -7.471    0.000000000000082 ***
factor(season)4           9.681355    1.380945   7.011    0.000000000002426 ***
factor(weather_main)2     4.007721    0.951161   4.214    0.000025223409140 ***
factor(weather_main)3    24.594350   11.632030   2.114               0.0345 *  
factor(weather_main)5    -4.027791   12.451190  -0.323               0.7463    
factor(weather_main)6    28.171788   51.870872   0.543               0.5871    
factor(weather_main)7    15.305422    6.127849   2.498               0.0125 *  
factor(weather_main)8    21.905840    2.156707  10.157 < 0.0000000000000002 ***
factor(weather_main)9    84.119876   32.820047   2.563               0.0104 *  
factor(weather_main)12   10.464185    7.360827   1.422               0.1552    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 73.33 on 27686 degrees of freedom
Multiple R-squared:  0.9176,    Adjusted R-squared:  0.9176 
F-statistic: 1.714e+04 on 18 and 27686 DF,  p-value: < 0.00000000000000022
  • OLS method
#ModelOLS <- ols_step_all_possible(workModelValenciaFull)
#cat("\n")
#plot(x = ModelOLS$n, y = ModelOLS$adjr)
#plot(x = ModelOLS$n, y = ModelOLS$aic)
#OLSsummary <- ModelOLS %>% group_by(n) %>%  filter(adjr == max(adjr))

Same reduced model as P-Value approach (thus commenting out given processing time)

Select the final model

Model adjusted R-square from above methods

  1. Full: 0.9177

  2. P-value: 0.9177

  3. Bestfits: 0.9174

  4. AIC Method: 0.9177

  5. Cross-validation: 0.9176

Final model is the workModelValenciaPvalue with highest Adjusted R-square.

Method <- c('Full','P-value', 'BestFits', 'AIC Forward', 'Corss Validation')
Variables <- c(13,12,8,12,9)
Coefficients <- c(23,22,18,22,19)
Adjusted_R <- c(0.9176974,0.9176977,0.9173514,0.9176977,0.9175922)
ValenciaTable<-data.frame(Method,Variables,Coefficients,Adjusted_R)

Build the Final Model

workFinal <- workModelValenciaPvalue

Check the linearity, heteroscedasticity, outlier and normal assumption

par(mfrow=c(2,2))
#Residual Plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
#norm test
std.res <- rstandard(workFinal)
ad.test(std.res)

    Anderson-Darling normality test

data:  std.res
A = 118.51, p-value < 0.00000000000000022
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
#leverage 
lev<-hatvalues(workFinal)
cutlev = (2*length(coefficients(workFinal)))/nrow(workValencia)
# Count and assess the number of leverage values > cut-off
potoutlier <- sum(lev > cutlev, na.rm=TRUE)
totcount = length(fitted(workFinal))
print(paste("The number of leverage points that are potential outliers is", potoutlier,".  This is", round(100*potoutlier/totcount,2),"% of the total number of predicted values, (", totcount,") thus not material."))
[1] "The number of leverage points that are potential outliers is 936 .  This is 3.38 % of the total number of predicted values, ( 27705 ) thus not material."
#barplot(lev, ylim = c(0, 2*cutlev))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for the working set")
abline(h = cutlev, col = "red", lty=2)

#cook distance
cookdist<-cooks.distance(workFinal)
#barplot(cookdist, ylim=c(0,1.01), main = "Cook's Distance plot")
#abline(h = 1, col = "red")
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.05,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")

# Opening the graphical device
# Customizing the output
pdf("ValenciaAssumptionCheck1.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
# par(mfrow=c(2,2))
#residuals scatter plot
plot(x=fitted(workFinal), y=studres(workFinal), xlab = "Fitted Values",
ylab = "Studentized Residuals", main='Residuals Plot', col = ifelse(studres(workFinal) < -3,"red",ifelse(studres(workFinal) > 3,"red","black")))
abline(h=-3, col="red", lty=2)
abline(h=3, col="red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#normality plot
pdf("ValenciaAssumptionCheck2.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
qqnorm(studres(workFinal), pch = 1, frame = FALSE, main=expression("AD test =2.2*10"^-16))
qqline(studres(workFinal), col = "steelblue", lwd = 2)
grid(10,10)
dev.off()
null device 
          1 
#leverage plot
pdf("ValenciaAssumptionCheck3.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for Barcelona working set")
abline(h = cutlev, col = "red", lty=2)
grid(10,10)
dev.off()
null device 
          1 
#Cook's distance plot
pdf("ValenciaAssumptionCheck4.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
ggplot(as.data.frame(cookdist),aes(x=1:nrow(as.data.frame(cookdist)),y=cookdist)) +
  geom_line() + 
  geom_hline(aes(yintercept = 1,color = "red"))+
  scale_y_continuous(limits = c(0,1.1))+
  scale_y_break(breaks = c(0.005,0.9), scales =0.1) +
  guides(color = "none") +
  labs(title="Cook's Distance",
        x ="Index", y = "")
dev.off()
null device 
          1 

Use the estimated model to predict the values in the evaluation set

newdata <- evalValencia[ c(-2,-14)]
predict.eval <- predict(workFinal, newdata)
plot(x = evalValencia$energy_demand, y=predict.eval, xlab="actual energy demand for the evaluation set",
ylab="predicted energy demand for the evaluation set", main = "Valencia")
abline(a=0, b=1, col="blue")
grid(10,10)

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("ValenciaEvalActComp.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,1))
plot(x = evalValencia$energy_demand, y=predict.eval, xlab="Actual energy demand for the evaluation set",
ylab="Predicted energy demand for the evaluation set", main = "Valencia", cex.lab = 0.75)
abline(a=0, b=1, col="blue")
grid(10,10)

# Closing the graphical device
dev.off()
null device 
          1 

Compute the RMSE for the predictions

rootmse <- rmse(evalValencia$energy_demand, predict.eval)
rootmse <- rmse(evaldata$energy_demand, predict.eval)
print(paste("The root mean square error of the predicted vs actual",nrow(evaldata),"hourly energy_data points for Valencia over the 2015-2018 timespan is an energy demand difference of",round(rootmse,2),"MWh."))
[1] "The root mean square error of the predicted vs actual 6927 hourly energy_data points for Valencia over the 2015-2018 timespan is an energy demand difference of 73.9 MWh."
cat("\n")
demandcompdf <- data.frame("Actual (MWh)" = evalValencia$energy_demand,"Predicted (MWh)" = predict.eval)
head(demandcompdf,4L)
cat("\n")
print(paste("For example, the first actual demand value is",round(evaldata$energy_demand[1],2),"MWh and the corresponding predicted value is",round(predict.eval[1],2),"MWh. The percent error between this predicted value relative to the actual value is",round((100*(predict.eval[1] - evaldata$energy_demand[1])/evaldata$energy_demand[1]),2),"%."))
[1] "For example, the first actual demand value is 1359.45 MWh and the corresponding predicted value is 1087.2 MWh. The percent error between this predicted value relative to the actual value is -20.03 %."
cat("\n")
diffpct <- c((abs(evaldata$energy_demand - predict.eval))/evaldata$energy_demand)
print(paste("The mean difference between the",nrow(evaldata),"evaluation data set actual and model predicted values is",round(100*mean(diffpct),2),"%."))
[1] "The mean difference between the 6927 evaluation data set actual and model predicted values is 3.6 %."

Compute and analyze Hat Matrix for Evaluation data set

Obtain X matrix from the model

Predictors <-rownames(summary(workFinal)$coefficients[,])
workpredictors <- as.data.frame(Predictors)
X <- model.matrix(workFinal)
class(X)
[1] "matrix" "array" 
cat("\n")
dim(X)
[1] 27705    22
cat("\n")
head(X, 4L)
  (Intercept)      E_1     E_25    temp humidity pressure wind_speed rain_duration factor(day_night)2 factor(time_band)2 factor(time_band)3
1           1 1359.453 1256.750 270.475       77     1001          1             0                  1                  0                  0
2           1 1115.686 1230.321 270.292       71     1004          2             0                  1                  0                  0
3           1 1136.149 1241.974 270.292       71     1004          2             0                  1                  0                  0
4           1 1120.369 1283.792 270.292       71     1004          2             0                  1                  0                  0
  factor(season)2 factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)5 factor(weather_main)6
1               0               0               1                     0                     0                     0                     0
2               0               0               1                     0                     0                     0                     0
3               0               0               1                     0                     0                     0                     0
4               0               0               1                     0                     0                     0                     0
  factor(weather_main)7 factor(weather_main)8 factor(weather_main)9 factor(weather_main)12
1                     0                     0                     0                      0
2                     0                     0                     0                      0
3                     0                     0                     0                      0
4                     0                     0                     0                      0

Create x_new matrix using the validation data. Note that the purpose of running the evaluation regression here is to extract all of the correct predictor coefficients.

evalFinal <- lm(energy_demand ~ E_1 + E_25 + temp + humidity + pressure + wind_speed + rain_duration + factor(day_night) + factor(time_band) + factor(season) + factor(weather_main), data = evalValencia)
Predictors <-rownames(summary(evalFinal)$coefficients[,])
evalpredictors <- as.data.frame(Predictors)
x_new1 <- model.matrix(evalFinal)
cat("\n")
dim(x_new1)
[1] 6927   22
cat("\n")
#Check if there's a predictor difference between the work and eval matrices
workevalcompare <-anti_join(workpredictors,evalpredictors)
Joining, by = "Predictors"
cat("\n")
#If workevalcompare is 'No data available in table', run the next line of code and skip to line 1960
x_new <- x_new1
#Otherwise when workevalcompare is not 'No data available in table', run the following lines of code after identifying which column is missing between X and x_new1 (no discrepancy was found between workValencia but not in evalValencia).
#x_new_ <-as.data.frame(cbind(x_new1,rep(0,nrow(x_new1))))
#dummy_xnew<- x_new_ %>% relocate(V21,.before = 16) %>% rename("factor(weather_main)4" = V21)
#x_new <- as.matrix(dummy_xnew)  
cat("\n")
class(x_new)
[1] "matrix" "array" 
cat("\n")
dim(x_new)
[1] 6927   22
cat("\n")
head(x_new, 4L)
  (Intercept)      E_1     E_25    temp humidity pressure wind_speed rain_duration factor(day_night)2 factor(time_band)2 factor(time_band)3
1           1 1109.831 1231.994 270.475       77     1001          1             0                  1                  0                  0
2           1 1312.896 1557.109 274.601       71     1005          1             0                  0                  0                  1
3           1 1345.570 1620.951 284.824       55     1006          1             0                  0                  0                  1
4           1 1562.295 1619.445 279.984       68     1035          1             0                  1                  0                  1
  factor(season)2 factor(season)3 factor(season)4 factor(weather_main)2 factor(weather_main)3 factor(weather_main)5 factor(weather_main)6
1               0               0               1                     0                     0                     0                     0
2               0               0               1                     0                     0                     0                     0
3               0               0               1                     0                     0                     0                     0
4               0               0               1                     0                     0                     0                     0
  factor(weather_main)7 factor(weather_main)8 factor(weather_main)9 factor(weather_main)12
1                     0                     0                     0                      0
2                     0                     0                     0                      0
3                     0                     0                     0                      0
4                     0                     0                     0                      0

Compute the hat values for the validation data

h_new_mid <- solve(t(X)%*%X)
dim(h_new_mid)
[1] 22 22
h_new <- x_new%*%h_new_mid%*%t(x_new)
dim(h_new)
[1] 6927 6927

Plot the hatvalues for the data in the evaluation set

#cutlev2 = (2*length(coefficients(evalFinal)))/nrow(evalValencia)
# Count and assess the number of leverage values > cut-off
potoutlier2 <- sum(diag(h_new) > cutlev, na.rm=TRUE)
totcount2 = nrow(evalValencia)
plot(diag(h_new), type = "h", ylab = "Leverage for the validation data set", ylim = c(0,2*cutlev), main = "Valencia")
abline(h=cutlev, col="red", lty=2)

print(paste("The number of leverage points that are potential outliers is", potoutlier2,".  This is", round(100*potoutlier2/totcount2,2),"% of the total number of predicted values, (", totcount2,") thus not material."))
[1] "The number of leverage points that are potential outliers is 232 .  This is 3.35 % of the total number of predicted values, ( 6927 ) thus not material."

Side-by-side comparision

par(mfrow=c(1,2))
plot(lev, type="h", ylim = c(0, 2*cutlev), ylab="Leverage for the working data set", main = "Valencia")
abline(h = cutlev, col = "red", lty=2)
plot(diag(h_new), type = "h", ylim = c(0,2*cutlev), ylab = "Leverage for the evaluation data set", main = "Valencia")
abline(h=cutlev, col="red", lty=2)

print(paste("The leverage cutoff exceedences in the evaluation set is",round(100*potoutlier2/totcount2,2),"% of the total number of observed values (", totcount2,") and is comparable to that of the working set,", round(100*potoutlier/totcount,2),"% of the total number of predicted values (", totcount,")."))
[1] "The leverage cutoff exceedences in the evaluation set is 3.35 % of the total number of observed values ( 6927 ) and is comparable to that of the working set, 3.38 % of the total number of predicted values ( 27705 )."

Prepare report figure

# Opening the graphical device
# Customizing the output
pdf("ValenciaLevComp.pdf",         # File name
    width = 14, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   
par(mfrow=c(1,2))
plot(lev, type="h", ylab = "Leverage", ylim = c(0,2*cutlev), yaxt = 'n', main = "Valencia Working Data", res =600)
abline(h = cutlev, col = "red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

plot(diag(h_new), type = "h", ylab = "", ylim = c(0,2*cutlev), yaxt = 'n', main = "Valencia Evaluation Data")
abline(h=cutlev, col="red", lty=2)
axis(side = 2, at=seq(0,2*round(cutlev,5),by=round(cutlev,5)))

# Closing the graphical device
dev.off()
null device 
          1 

Tree Based Modelling

Code for Barcelona

organize data

# import data raw data set
w_BCN_raw <- read.csv("data/working_Barcelona.csv")
e_BCN_raw <- read.csv("data/evaluation_Barcelona.csv")
w_BCN <- w_BCN_raw[,-c(1,2)]
e_BCN <- e_BCN_raw[,-c(1,2)]
# changes of data type
class(w_BCN$day_night) = "category"
class(w_BCN$time_band) = "category"
class(w_BCN$season) = "category"
class(w_BCN$weather_main) = "category"

class(e_BCN$day_night) = "category"
class(e_BCN$time_band) = "category"
class(e_BCN$season) = "category"
class(e_BCN$weather_main) = "category"

# check data types
str(w_BCN)
str(e_BCN) 
summary(w_BCN)

RF modeling

# fit random forest model
# default RF model
set.seed(1234)
rf <- randomForest(formula = energy_demand ~ ., data = w_BCN)

# find the num of trees in the forest with smallest MSE
plot(rf)
grid(10,10)
summary(rf)

testBCN <- rf$mse
testBCN <- as.data.frame(testBCN)
threshold = 0.05 # Threshold Error Margin (5 %) 

#Visually, the slope become flat around 100 in the plot, divide test dataset at 100
InflectionValue <- testBCN[nrow(testBCN),] + threshold*testBCN[nrow(testBCN),]

InflectionIndex <- length(testBCN[testBCN>InflectionValue]) + 1
# modify the model with best mtry and importance of the predictors

mtry <- tuneRF(w_BCN,w_BCN$energy_demand, ntreeTry=InflectionIndex,
              stepFactor=1.5,improve=0.01, trace=TRUE, plot=TRUE)
best.m <- mtry[mtry[, 2] == min(mtry[, 2]), 1]

rf_modified <-randomForest(energy_demand~.,data=w_BCN, mtry = best.m, importance=TRUE,ntree=InflectionIndex)



varImpPlot(randomForest(energy_demand~.,data=w_BCN, mtry = best.m, importance=TRUE,ntree=InflectionIndex), main = "Importance of the variables", n.var = 13)

pdf("VarImportance_BCN.pdf",         # File name
    width = 14, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model
   

varImpPlot(randomForest(energy_demand~.,data=w_BCN, mtry = best.m, importance=TRUE,ntree=InflectionIndex), main = "Importance of the variables", n.var = 13)
# Closing the graphical device
dev.off()

check performance

# check the time
# system.time(randomForest(energy_demand~.,data=w_BCN, mtry = best.m, importance=TRUE,ntree=InflectionIndex))
# assess the test set performance of rf_modified with Metrics library
predvalue <- predict(rf_modified, e_BCN)
rmse(e_BCN$energy_demand, predvalue)

pdf("BCN1.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(e_BCN$energy_demand, predvalue, xlab = "Actual energy demand for the evaluation set ", ylab = "Predicted energy demand for the evaluation set", main = "Barcelona (Tree)", cex.lab=0.75)
abline(a=0,b=1,col="blue")
grid(10,10)
dev.off()
pred.eval <- predvalue
diffpct <- c((abs(evaldata$energy_demand - predict.eval))/evaldata$energy_demand)
print(paste("The mean difference between the",nrow(evaldata),"evaluation data set actual and model predicted values is",round(100*mean(diffpct),2),"%."))
[1] "The mean difference between the 6927 evaluation data set actual and model predicted values is 3.6 %."

ranger modeling

# create RF model with ranger package
ranger(formula = energy_demand ~ ., data = w_BCN)

check performance

# check execution time
system.time(rf_ranger <- ranger(formula = energy_demand ~ ., data = w_BCN))
# performance
rmse(
  e_BCN$energy_demand, 
  predict(rf_ranger, e_BCN)$predictions
  )

pdf("BCN2.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(e_BCN$energy_demand, 
  predict(rf_ranger, e_BCN)$predictions, ylab = "Prediction", xlab = "Energy Demand of Test Set", main = "Ranger for Barcelona")
grid(10,10)
dev.off()

Code for Bilbao

organize data

# import data raw data set
w_BLB_raw <- read.csv("data/working_Bilbao.csv")
e_BLB_raw <- read.csv("data/evaluation_Bilbao.csv")
w_BLB <- w_BLB_raw[,-c(1,2)]
e_BLB <- e_BLB_raw[,-c(1,2)]
# changes of data type
class(w_BLB$day_night) = "category"
class(w_BLB$time_band) = "category"
class(w_BLB$season) = "category"
class(w_BLB$weather_main) = "category"

class(e_BLB$day_night) = "category"
class(e_BLB$time_band) = "category"
class(e_BLB$season) = "category"
class(e_BLB$weather_main) = "category"

# check data types
str(w_BLB)
str(e_BLB) 
summary(w_BLB)

RF modeling

# fit random forest model
# default RF model
set.seed(1234)
rf2 <- randomForest(formula = energy_demand ~ ., data = w_BLB)

# find the num of trees in the forest with smallest MSE
plot(rf2)

summary(rf2)
n2 <- which.min(rf2$mse)

testBLB <- rf2$mse
testBLB <- as.data.frame(testBLB)


threshold = 0.05 # Threshold Error Margin (5 %) 

#Visually, the slope become flat around 100 in the plot, divide test dataset at 100
InflectionValue <- testBLB[nrow(testBLB),] + threshold*testBLB[nrow(testBLB),]

InflectionIndex <- length(testBLB[testBLB>InflectionValue]) + 1
# modify the model with best mtry and importance of the predictors

mtry <- tuneRF(w_BLB,w_BLB$energy_demand, ntreeTry=InflectionIndex,
              stepFactor=1.5,improve=0.01, trace=TRUE, plot=TRUE)
best.m <- mtry[mtry[, 2] == min(mtry[, 2]), 1]

rf2_modified <-randomForest(energy_demand~.,data=w_BLB, mtry = best.m, importance=TRUE,ntree=InflectionIndex)


varImpPlot(randomForest(energy_demand~.,data=w_BLB, mtry = best.m, importance=TRUE,ntree=InflectionIndex), main = "Importance of the varibales", n.var = 13)

check performance

# check the time
system.time(randomForest(formula = energy_demand ~ ., 
                   data = w_BLB, ntree = n2, na.action=na.exclude))
# assess the test set performance of rf_modified with Metrics library
predvalue <- predict(rf2_modified, e_BLB)
rmse(e_BLB$energy_demand, predvalue)

pdf("BLB1.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(e_BLB$energy_demand, predvalue, xlab = "Energy Demand of Test Set ", ylab = "Prediction", main = "rF for Bilbao")
grid(10,10)
dev.off()



plot(e_BLB$energy_demand, predvalue, xlab = "Energy Demand of Test Set ", ylab = "Prediction", main = "rF for Bilbao")

ranger modeling

# create RF model with ranger package
rf2_ranger <- ranger(formula = energy_demand ~ ., data = w_BLB)

check performance

# check execution time
# system.time(rf2_ranger <- ranger(formula = energy_demand ~ ., data = w_BLB))
# performance
rmse(
  e_BLB$energy_demand, 
  predict(rf2_ranger, e_BLB)$predictions
  )

pdf("BLB2.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(
  e_BLB$energy_demand, 
  predict(rf2_ranger, e_BLB)$predictions, ylab = "Prediction", xlab = "Energy Demand of Test Set", main = "Ranger for Bilbao"
  )
grid(10,10)
dev.off()


plot(
  e_BLB$energy_demand, 
  predict(rf2_ranger, e_BLB)$predictions, ylab = "Prediction", xlab = "Energy Demand of Test Set", main = "Ranger for Bilbao"
  )

Code for Madrid

organize data

# import data raw data set
w_MDD_raw <- read.csv("data/working_Madrid.csv")
e_MDD_raw <- read.csv("data/evaluation_Madrid.csv")
w_MDD <- w_MDD_raw[,-c(1,2)]
e_MDD <- e_MDD_raw[,-c(1,2)]
# changes of data type
class(w_MDD$day_night) = "category"
class(w_MDD$time_band) = "category"
class(w_MDD$season) = "category"
class(w_MDD$weather_main) = "category"

class(e_MDD$day_night) = "category"
class(e_MDD$time_band) = "category"
class(e_MDD$season) = "category"
class(e_MDD$weather_main) = "category"

# check data types
str(w_MDD)
str(e_MDD) 
summary(w_MDD)

RF modeling

# fit random forest model
# default RF model
set.seed(1234)
rf3 <- randomForest(formula = energy_demand ~ ., data = w_MDD)

# find the num of trees in the forest with smallest MSE
plot(rf3)

summary(rf3)

testMDD <- rf3$mse
testMDD <- as.data.frame(testMDD)

#the slope become flat around 100 in the plot, divide test dataset at 100

threshold = 0.05 # Threshold Error Margin (5 %) 

#Visually, the slope become flat around 100 in the plot, divide test dataset at 100
InflectionValue <- testMDD[nrow(testMDD),] + threshold*testMDD[nrow(testMDD),]

InflectionIndex <- length(testMDD[testMDD>InflectionValue]) + 1
# modify the model with best mtry and importance of the predictors

mtry <- tuneRF(w_MDD, w_MDD$energy_demand, ntreeTry=InflectionIndex,
              stepFactor=1.5,improve=0.01, trace=TRUE, plot=TRUE)
best.m <- mtry[mtry[, 2] == min(mtry[, 2]), 1]

rf3_modified <-randomForest(energy_demand~.,data=w_MDD, mtry = best.m, importance=TRUE,ntree=InflectionIndex)


varImpPlot(randomForest(energy_demand~.,data=w_MDD, mtry = best.m, importance=TRUE,ntree=InflectionIndex), main = "Importance of the variables", n.var = 13)

check performance

# check the time
#system.time(randomForest(formula = energy_demand ~ ., 
  #                 data = w_MDD, ntree = 100, na.action=na.exclude))
# assess the test set performance of rf_modified with Metrics library
predvalue <- predict(rf3_modified, e_MDD)
rmse(e_MDD$energy_demand, predvalue)

pdf("MDD1.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(e_MDD$energy_demand, predvalue, ylab = "Prediction", xlab = "Energy Demand of Test Set", main = "rF for Madrid")

grid(10,10)
dev.off()

plot(e_MDD$energy_demand, predvalue, ylab = "Prediction", xlab = "Energy Demand of Test Set", main = "rF for Madrid")

ranger modeling

# create RF model with ranger package
rf3_ranger <- ranger(formula = energy_demand ~ ., data = w_MDD)

check performance

# check execution time
# system.time(rf3_ranger <- ranger(formula = energy_demand ~ ., data = w_MDD))
# performance
rmse(
  e_MDD$energy_demand, 
  predict(rf3_ranger, e_MDD)$predictions
  )

pdf("MDD2.pdf",         # File name
    width = 7, height = 4, # Width and height in inches
    bg = "white",          # Background color
    colormodel = "cmyk")  # Color model

plot(
  e_MDD$energy_demand, 
  predict(rf2_ranger, e_MDD)$predictions, ylab = "Prediction", xlab = "Energy Demand of Test Set", main = "Ranger for Madrid"
  )
grid(10,10)
dev.off()

plot(
  e_MDD$energy_demand, 
  predict(rf3_ranger, e_MDD)$predictions, ylab = "Prediction", xlab = "Energy Demand of Test Set", main = "Ranger for Madrid"
  )

Code for Seville

organize data

# import data raw data set
w_SVL_raw <- read.csv("data/working_Seville.csv")
e_SVL_raw <- read.csv("data/evaluation_Seville.csv")
w_SVL <- w_SVL_raw[,-c(1,2)]
e_SVL <- e_SVL_raw[,-c(1,2)]
# changes of data type
class(w_SVL$day_night) = "category"
class(w_SVL$time_band) = "category"
class(w_SVL$season) = "category"
class(w_SVL$weather_main) = "category"

class(e_SVL$day_night) = "category"
class(e_SVL$time_band) = "category"
class(e_SVL$season) = "category"
class(e_SVL$weather_main) = "category"

# check data types
str(w_SVL)
str(e_SVL) 
summary(w_SVL)

RF modeling

# fit random forest model
# default RF model
set.seed(1234)
rf4 <- randomForest(formula = energy_demand ~ ., data = w_SVL)
# find the num of trees in the forest with smallest MSE
plot(rf4)

testSVL <- rf4$mse
testSVL <- as.data.frame(testSVL)

#the slope become flat around 100 in the plot, divide test dataset at 100

threshold = 0.05 # Threshold Error Margin (5 %) 

#Visually, the slope become flat around 100 in the plot, divide test dataset at 100
InflectionValue <- testSVL[nrow(testSVL),] + threshold*testSVL[nrow(testSVL),]

InflectionIndex <- length(testSVL[testSVL>InflectionValue]) + 1
# modify the model with best mtry and importance of the predictors

mtry <- tuneRF(w_SVL, w_SVL$energy_demand, ntreeTry=InflectionIndex,
              stepFactor=1.5,improve=0.01, trace=TRUE, plot=TRUE)
best.m <- mtry[mtry[, 2] == min(mtry[, 2]), 1]

rf4_modified <-randomForest(energy_demand~.,data=w_SVL, mtry = best.m, importance=TRUE,ntree=InflectionIndex)


varImpPlot(randomForest(energy_demand~.,data=w_SVL, mtry = best.m, importance=TRUE,ntree=InflectionIndex), main = "Importance of the variables", n.var = 13)

check performance

# check the time
# system.time(randomForest(formula = energy_demand ~ ., 
#                 data = w_SVL, ntree = n4, na.action=na.exclude))
# assess the test set performance of rf_modified with Metrics library
predvalue <- predict(rf4_modified, e_SVL)
rmse(e_SVL$energy_demand, predvalue)
plot(e_SVL$energy_demand, predvalue, main = "Seville1")

ranger modeling

# create RF model with ranger package
rf4_ranger <- ranger(formula = energy_demand ~ ., data = w_SVL)

check performance

# check execution time
# system.time(rf4_ranger <- ranger(formula = energy_demand ~ ., data = w_SVL))
# performance
rmse(
  e_SVL$energy_demand, 
  predict(rf4_ranger, e_SVL)$predictions
  )
plot(
  e_SVL$energy_demand, 
  predict(rf4_ranger, e_SVL)$predictions, xlab = "predvalue", main = "Seville2"
  )

Code for Valencia

organize data

# import data raw data set
w_VLC_raw <- read.csv("data/working_Valencia.csv")
e_VLC_raw <- read.csv("data/evaluation_Valencia.csv")
w_VLC <- w_VLC_raw[,-c(1,2)]
e_VLC <- e_VLC_raw[,-c(1,2)]
# changes of data type
class(w_VLC$day_night) = "category"
class(w_VLC$time_band) = "category"
class(w_VLC$season) = "category"
class(w_VLC$weather_main) = "category"

class(e_VLC$day_night) = "category"
class(e_VLC$time_band) = "category"
class(e_VLC$season) = "category"
class(e_VLC$weather_main) = "category"

# check data types
str(w_VLC)
str(e_VLC) 
summary(w_VLC)

RF modeling

# fit random forest model
# default RF model
set.seed(1234)
rf5 <- randomForest(formula = energy_demand ~ ., data = w_VLC)
# find the num of trees in the forest with smallest MSE
plot(rf5)

testVLC <- rf5$mse
testVLC <- as.data.frame(testVLC)

#the slope become flat around 100 in the plot, divide test dataset at 100

threshold = 0.05 # Threshold Error Margin (5 %) 

#Visually, the slope become flat around 100 in the plot, divide test dataset at 100
InflectionValue <- testVLC[nrow(testVLC),] + threshold*testVLC[nrow(testVLC),]

InflectionIndex <- length(testVLC[testVLC>InflectionValue]) + 1
# modify the model with best mtry and importance of the predictors

mtry <- tuneRF(w_VLC, w_VLC$energy_demand, ntreeTry=InflectionIndex,
              stepFactor=1.5,improve=0.01, trace=TRUE, plot=TRUE)
best.m <- mtry[mtry[, 2] == min(mtry[, 2]), 1]

rf5_modified <-randomForest(energy_demand~.,data=w_VLC, mtry = best.m, importance=TRUE,ntree=InflectionIndex)


varImpPlot(randomForest(energy_demand~.,data=w_VLC, mtry = best.m, importance=TRUE,ntree=InflectionIndex), main = "Importance of the variables", n.var = 13)

check performance

# check the time
# system.time(randomForest(formula = energy_demand ~ ., 
   #                 data = w_VLC, ntree = n5, na.action=na.exclude))
# assess the test set performance of rf_modified with Metrics library
predvalue <- predict(rf5_modified, e_VLC)
rmse(e_VLC$energy_demand, predvalue)
plot(e_VLC$energy_demand, predvalue, main = "Valencia1")

ranger modeling

# create RF model with ranger package
rf5_ranger <- ranger(formula = energy_demand ~ ., data = w_VLC)

check performance

# check execution time
# system.time(rf5_ranger <- ranger(formula = energy_demand ~ ., data = w_VLC))
# performance
rmse(
  e_VLC$energy_demand, 
  predict(rf5_ranger, e_VLC)$predictions
  )
plot(
  e_VLC$energy_demand, 
  predict(rf5_ranger, e_VLC)$predictions, xlab = "predvalue", main = "Valencia2"
  )
LS0tCnRpdGxlOiAiU1RBVCA1MDEgUHJvamVjdCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCiMgIE11bHRpcGxlIExpbmVhciAgUmVncmVzc2lvbiBNb2RlbCB0byBQcmVkaWN0IEVsZWN0cmljaXR5IERlbWFuZCBvZiBGaXZlIENpdGllcyBpbiBTcGFpbgojIyBMaW5rcyBkb2N1bWVudGF0aW9uCgoqIFtHaXRodWIgQ29kZV0oaHR0cHM6Ly9naXRodWIuY29tL1NoYWlsZXNoV2FzdGkvU1RBVC01MDEtUHJvamVjdC5naXQpCiogW092ZXJsZWFmIFJlcG9ydF0oaHR0cHM6Ly93d3cub3ZlcmxlYWYuY29tLzk2OTgzMjE5OTNqdndjamJkdHltemspCiogW0thZ2dsZSBEYXRhXShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL25pY2hvbGFzamhhbmEvZW5lcmd5LWNvbnN1bXB0aW9uLWdlbmVyYXRpb24tcHJpY2VzLWFuZC13ZWF0aGVyP3NlbGVjdD13ZWF0aGVyX2ZlYXR1cmVzLmNzdikKCk5vdGU6IElmIHlvdSBuZWVkIHBlcm1pc3Npb24gdG8gdXNlIHNvbWUgbGlua3MsIHBsZWFzZSBlbWFpbCBhdDogKnNqdzYyMzZAcHN1LmVkdSogCgojIyBJbnN0YWxsIGFsbCBQYWNrYWdlcyBhbmQgSW1wb3J0IExpYnJhcnkgCgojIyMgUGFja2FnZXMgaW5zdGFsbGF0aW9uCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikKIyBpbnN0YWxsLnBhY2thZ2VzKCdjYXInKQojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQoKYGBgCgojIyMgSW1wb3J0IGxpYnJhcnkKYGBge3IsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CiNsb2FkIHBhY2thZ2UgaGVyZQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShncmlkQmFzZSkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGNhcikKbGlicmFyeSAoR0dhbGx5KQpsaWJyYXJ5KHJlYWR4bCkgCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShicm9vbSkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoTUFTUykKbGlicmFyeShTdGF0MkRhdGEpCmxpYnJhcnkob2xzcnIpCmxpYnJhcnkobGVhcHMpCmxpYnJhcnkobGVhcHMpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkobG10ZXN0KQpsaWJyYXJ5KG5vcnRlc3QpCmxpYnJhcnkoTWV0cmljcykKbGlicmFyeShicm9vbS5taXhlZCkKbGlicmFyeShnZ2JyZWFrKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeSh0aW55dGV4KQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkgCmxpYnJhcnkocmFuZ2VyKQpvcHRpb25zKHNjaXBlbj05OTkpICAjIHR1cm4tb2ZmIHNjaWVudGlmaWMgbm90YXRpb24gbGlrZSAxZSs0OAp0aGVtZV9zZXQodGhlbWVfYncoKSkgICMgcHJlLXNldCB0aGUgYncgdGhlbWUuCgpgYGAKCiMjIERhdGEgUHJvY2Vzc2luZwojIyMgTG9hZCB3ZWF0aGVyIGRhdGEKYGBge3J9CiMjIFJlYWQgcmF3IGRhdGE6IHdlYXRoZXIgZmVhdHVyZXMKd2VhdGhlcl9yYXc8LXJlYWQuY3N2KGZpbGUgPSAiRGF0YS93ZWF0aGVyX2ZlYXR1cmVzLmNzdiIpCmhlYWQod2VhdGhlcl9yYXcsNCkgCmBgYAoKCmBgYHtyfQojIEFsbCB0aGUgY29sdW1ucyBpbiB0aGUgcmF3IGRhdGEKd2VhdGhlcl9yYXdfZmllbGRzIDwtIGNvbG5hbWVzKHdlYXRoZXJfcmF3KSAKcHJpbnQod2VhdGhlcl9yYXdfZmllbGRzKQpgYGAKCiMjIyBSZW1vdmUgYWxsIGR1cGxpY2F0ZSBkYXRhIGZyb20gdGhlIHdlYXRoZXIgZGF0YSAKCmBgYHtyfQojIEFsbCB0aGUgZHVwbGljYXRlIGRhdGEgYmFzZWQgb24gZGF0ZSAKd2VhdGhlcl9kdXBsaWNhdGU8LXdlYXRoZXJfcmF3W2R1cGxpY2F0ZWQod2VhdGhlcl9yYXdbLDE6Ml0pLF0Kd2VhdGhlciA8LSB3ZWF0aGVyX3Jhd1shZHVwbGljYXRlZCh3ZWF0aGVyX3Jhd1ssMToyXSksIF0KYGBgCgojIyMgTG9hZCBlbmVyZ3kgZGF0YSBzZXQgbW9kaWZpZWQgZnJvbSBLYWdlbAojIyMjIENhbGN1bGF0aW9uOiAKKihLZWl0aCxDYW4geW91IGV4cGxhaW4gaGVyZSBpbiBidWxsZXRzIGhvdyBuZXcgZXhjZWwgc2hlZXQgaXMgZ2VuZXJhdGVkIGluIGJ1bGxldD8pKgoqIFVzZSAkJCBmb3IgbWF0aCBlcXVhdGlvbiwgaWYgYW55IGFuZCByZWZlcmVuY2UgdGhlIG9yaWdpbmFsIGRhdGEsIGFuZCBvdGhlciBsaW5rcyBsaWtlIEkgZGlkIGluIHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBjb2RlCgoKYGBge3J9CmVuZXJneV9yYXc8LXJlYWRfZXhjZWwoIkRhdGEvZW5lcmd5X2RhdGFzZXQtS08ueGxzeCIpCmBgYAoKCiogRml2ZSBjaXRpZXMgYXJlOgogICsgVmFsZW5jaWEKICArIEJhcmNlbG9uYQogICsgQmlsYmFvCiAgKyBTZXZpbGxlCiAgKyBNYWRyaWQKCgojIyMgTWVyZ2UgRW5lcmd5IERlbWFuZCBhbmQgV2VhdGhlciB0byBvbmUgZGF0YSBzZXQgYmFzZWQgb24gY2l0aWVzCmBgYHtyfQojIFZhbGVuY2lhCldlYXRoZXJfVmFsZW5jaWEgPC0gd2VhdGhlclt3ZWF0aGVyJGNpdHlfbmFtZSA9PSAnVmFsZW5jaWEnLF0KRGF0YV9WYWxlbmNpYSA8LW1lcmdlKFdlYXRoZXJfVmFsZW5jaWEsIGVuZXJneV9yYXdbLGMoInRpbWUiLCJWYWxlbmNpYSIpXSwgYnkueCA9ICJkdF9pc28iLCBieS55ID0gInRpbWUiKQpjb2xuYW1lcyhEYXRhX1ZhbGVuY2lhKVtjb2xuYW1lcyhEYXRhX1ZhbGVuY2lhKSA9PSAiVmFsZW5jaWEiXSA8LSAiZW5lcmd5IgoKIyBCYXJjZWxvbmEKV2VhdGhlcl9CYXJjZWxvbmEgPC0gd2VhdGhlclt3ZWF0aGVyJGNpdHlfbmFtZSA9PSAiIEJhcmNlbG9uYSIsXQpEYXRhX0JhcmNlbG9uYSA8LW1lcmdlKFdlYXRoZXJfQmFyY2Vsb25hLCBlbmVyZ3lfcmF3WyxjKCJ0aW1lIiwiQmFyY2Vsb25hIildLCBieS54ID0gImR0X2lzbyIsIGJ5LnkgPSAidGltZSIpCmNvbG5hbWVzKERhdGFfQmFyY2Vsb25hKVtjb2xuYW1lcyhEYXRhX0JhcmNlbG9uYSkgPT0gIkJhcmNlbG9uYSJdIDwtICJlbmVyZ3kiCgojIEJpbGJhbyAKV2VhdGhlcl9CaWxiYW8gPC0gd2VhdGhlclt3ZWF0aGVyJGNpdHlfbmFtZSA9PSAnQmlsYmFvJyxdCkRhdGFfQmlsYmFvIDwtbWVyZ2UoV2VhdGhlcl9CaWxiYW8sIGVuZXJneV9yYXdbLGMoInRpbWUiLCJCaWxib2EiKV0sIGJ5LnggPSAiZHRfaXNvIiwgYnkueSA9ICJ0aW1lIikKY29sbmFtZXMoRGF0YV9CaWxiYW8pW2NvbG5hbWVzKERhdGFfQmlsYmFvKSA9PSAiQmlsYm9hIl0gPC0gImVuZXJneSIKICAKIyBTZXZpbGxlCldlYXRoZXJfU2V2aWxsZSA8LSB3ZWF0aGVyW3dlYXRoZXIkY2l0eV9uYW1lID09ICdTZXZpbGxlJyxdCkRhdGFfU2V2aWxsZSA8LW1lcmdlKFdlYXRoZXJfU2V2aWxsZSwgZW5lcmd5X3Jhd1ssYygidGltZSIsIlNldmlsbGUiKV0sIGJ5LnggPSAiZHRfaXNvIiwgYnkueSA9ICJ0aW1lIikKY29sbmFtZXMoRGF0YV9TZXZpbGxlKVtjb2xuYW1lcyhEYXRhX1NldmlsbGUpID09ICJTZXZpbGxlIl0gPC0gImVuZXJneSIKCiMgTWFkcmlkCldlYXRoZXJfTWFkcmlkIDwtIHdlYXRoZXJbd2VhdGhlciRjaXR5X25hbWUgPT0gJ01hZHJpZCcsXQpEYXRhX01hZHJpZCA8LW1lcmdlKFdlYXRoZXJfTWFkcmlkLCBlbmVyZ3lfcmF3WyxjKCJ0aW1lIiwiTWFkcmlkIildLCBieS54ID0gImR0X2lzbyIsIGJ5LnkgPSAidGltZSIpCmNvbG5hbWVzKERhdGFfTWFkcmlkKVtjb2xuYW1lcyhEYXRhX01hZHJpZCkgPT0gIk1hZHJpZCJdIDwtICJlbmVyZ3kiCmBgYAoKIyMjIFdyaXRlIGFsbCB0aGUgZGF0YSBpbiBzZXBhcmF0ZSBjaXR5bmFtZS5jc3Ygc2hlZXRzCgpgYGB7cn0Kd3JpdGUuY3N2KERhdGFfQmFyY2Vsb25hLCJEYXRhL0JhcmNlbG9uYS5jc3YiLCByb3cubmFtZXMgPSBUUlVFKQp3cml0ZS5jc3YoRGF0YV9WYWxlbmNpYSwiRGF0YS9WYWxlbmNpYS5jc3YiLCByb3cubmFtZXMgPSBUUlVFKQp3cml0ZS5jc3YoRGF0YV9CaWxiYW8sIkRhdGEvQmlsYmFvLmNzdiIsIHJvdy5uYW1lcyA9IFRSVUUpCndyaXRlLmNzdihEYXRhX1NldmlsbGUsIkRhdGEvU2V2aWxsZS5jc3YiLCByb3cubmFtZXMgPSBUUlVFKQp3cml0ZS5jc3YoRGF0YV9NYWRyaWQsIkRhdGEvTWFkcmlkLmNzdiIsIHJvdy5uYW1lcyA9IFRSVUUpCgpgYGAKCgoKCiMjIEJBUkNFTE9OQSBEQVRBCmBgYHtyfQojIERhdGEgd2l0aCBhbGwgcHJlZGljdG9ycyBhbmQgcmVzcG9uc2UgdmFyaWFibGUgCiMjIFJlYWQgcmF3IGRhdGE6IEJhcmNlbG9uYQpCYXJjZWxvbmFfcmF3PC1yZWFkLmNzdihmaWxlID0gIkRhdGEvQmFyY2Vsb25hLmNzdiIpCgojIFJlbmFtZSBjb2x1bW4gbmFtZXMgCmNvbG5hbWVzKEJhcmNlbG9uYV9yYXcpW2NvbG5hbWVzKEJhcmNlbG9uYV9yYXcpID09ICJYIl0gPC0gIkRhdGFJRCIKY29sbmFtZXMoQmFyY2Vsb25hX3JhdylbY29sbmFtZXMoQmFyY2Vsb25hX3JhdykgPT0gImR0X2lzbyJdIDwtICJ0aW1lIgoKIyBTZWxlY3Qgb25seSB0aGUgY29sdW1ucyBvZiBpbnRlcmVzdAoKQmFyY2Vsb25hX0RhdGE8LUJhcmNlbG9uYV9yYXcgJT4lIAogIGRwbHlyOjpzZWxlY3QoRGF0YUlELHRpbWUsdGVtcCxodW1pZGl0eSxwcmVzc3VyZSx3aW5kX3NwZWVkLHJhaW5fMWgscmFpbl8zaCxzbm93XzNoLHdlYXRoZXJfbWFpbixlbmVyZ3kpCmBgYAoKIyMjIFNlZSBhbGwgdGhlIHdlYXRoZXIgZGVzY3JpcHRpb24gaW4gd2VhdGhlcl9tYWluCmBgYHtyfQpwcmludCh1bmlxdWUoQmFyY2Vsb25hX0RhdGEkd2VhdGhlcl9tYWluKSkKYGBgCgojIyMgR2l2ZSBpbnRlZ2VyIHZhbHVlcyBmb3IgZWFjaCBkZXNjcmlwdGlvbgoKYGBge3J9CkJhcmNlbG9uYV9EYXRhPC10cmFuc2Zvcm0oQmFyY2Vsb25hX0RhdGEsIHdlYXRoZXJfbWFpbiA9IGZhY3Rvcih3ZWF0aGVyX21haW4sIAogICAgICBsZXZlbHMgPSBjKCJjbGVhciIsICJjbG91ZHMiLCAiZHJpenpsZSIsImR1c3QiLCJmb2ciLCJoYXplIiwibWlzdCIsInJhaW4iLCJzbW9rZSIsInNub3ciLCJzcXVhbGwiLCJ0aHVuZGVyc3Rvcm0iKSwKICAgICAgIGxhYmVscyA9IGMoMToxMikpKQoKV2VhdGhlcl9kZXNjcmlwdGlvbiA8LSBkYXRhLmZyYW1lIChXZWF0aGVyX0Rlc2NyaXB0aW9uICA9IGMoImNsZWFyIiwgImNsb3VkcyIsICJkcml6emxlIiwKICAgICAgICAgICAgICAiZHVzdCIsImZvZyIsImhhemUiLCJtaXN0IiwicmFpbiIsInNtb2tlIiwic25vdyIsInNxdWFsbCIsInRodW5kZXJzdG9ybSIpLAogICAgICAgICAgICAgICAgICBJbmRleCA9IGMoMToxMikKICAgICAgICAgICAgICAgICAgKQpwcmludChXZWF0aGVyX2Rlc2NyaXB0aW9uKQpgYGAKCgojIyMgTW9yZSBEYXRhIFByb2Nlc3NpbmcKCgpgYGB7cn0KIyBBZGQgdHdvIGNvbHVtbnMgb2YgcmFpbiBkdXJhdGlvbgpCYXJjZWxvbmFfRGF0YVsicmFpbl9kdXJhdGlvbiJdIDwtIEJhcmNlbG9uYV9EYXRhJHJhaW5fMWggKyBCYXJjZWxvbmFfRGF0YSRyYWluXzNoIAoKIyBSZW5hbWUgQ29sdW1uIE5hbWVzIGZvciBjbGFyaXR5IApCYXJjZWxvbmFfRGF0YSA8LW1lcmdlKEJhcmNlbG9uYV9EYXRhLCBlbmVyZ3lfcmF3WyxjKCJ0aW1lIiwiU3BlY2lmaWVkIENhdGVnb3JpY2FsIFZhcmlhYmxlOlxyXG5UaW1lIEJhbmQgKFBlYWssIE9mZi1QZWFrLCBNaWQtUGVhaykiKV0sIGJ5LnggPSAidGltZSIsIGJ5LnkgPSAidGltZSIpCmNvbG5hbWVzKEJhcmNlbG9uYV9EYXRhIClbY29sbmFtZXMoQmFyY2Vsb25hX0RhdGEgKSA9PSAiU3BlY2lmaWVkIENhdGVnb3JpY2FsIFZhcmlhYmxlOlxyXG5UaW1lIEJhbmQgKFBlYWssIE9mZi1QZWFrLCBNaWQtUGVhaykiXSA8LSAidGltZV9iYW5kIgoKQmFyY2Vsb25hX0RhdGEgPC1tZXJnZShCYXJjZWxvbmFfRGF0YSwgZW5lcmd5X3Jhd1ssYygidGltZSIsIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuU2Vhc29uIChTcHJpbmcsIFN1bW1lciwgQXV0dW1uLCBXaW50ZXIpIildLCBieS54ID0gInRpbWUiLCBieS55ID0gInRpbWUiKQpjb2xuYW1lcyhCYXJjZWxvbmFfRGF0YSApW2NvbG5hbWVzKEJhcmNlbG9uYV9EYXRhICkgPT0gIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuU2Vhc29uIChTcHJpbmcsIFN1bW1lciwgQXV0dW1uLCBXaW50ZXIpIl0gPC0gInNlYXNvbiIKCkJhcmNlbG9uYV9EYXRhIDwtbWVyZ2UoQmFyY2Vsb25hX0RhdGEsIGVuZXJneV9yYXdbLGMoInRpbWUiLCJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblRpbWUgb2YgZGF5IChEYXkgdnMgTmlnaHQpIildLCBieS54ID0gInRpbWUiLCBieS55ID0gInRpbWUiKQpjb2xuYW1lcyhCYXJjZWxvbmFfRGF0YSApW2NvbG5hbWVzKEJhcmNlbG9uYV9EYXRhICkgPT0gIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBvZiBkYXkgKERheSB2cyBOaWdodCkiXSA8LSAiZGF5X25pZ2h0IgoKCmNvbG5hbWVzKEJhcmNlbG9uYV9EYXRhIClbY29sbmFtZXMoQmFyY2Vsb25hX0RhdGEgKSA9PSAic25vd18zaCJdIDwtICJzbm93X2R1cmF0aW9uIgoKQmFyY2Vsb25hX0RhdGE8LXN1YnNldChCYXJjZWxvbmFfRGF0YSxzZWxlY3Q9LWMocmFpbl8xaCxyYWluXzNoLERhdGFJRCkpCgpjb2xuYW1lcyhCYXJjZWxvbmFfRGF0YSApW2NvbG5hbWVzKEJhcmNlbG9uYV9EYXRhICkgPT0gInRpbWUiXSA8LSAidGltZV9JRCIKCkJhcmNlbG9uYV9EYXRhIDwtIEJhcmNlbG9uYV9EYXRhIFssIGMoInRpbWVfSUQiLCJ0ZW1wIiwiaHVtaWRpdHkiLCJwcmVzc3VyZSIsIndpbmRfc3BlZWQiLCJyYWluX2R1cmF0aW9uIiwic25vd19kdXJhdGlvbiIsImRheV9uaWdodCIsInRpbWVfYmFuZCIsInNlYXNvbiIsIndlYXRoZXJfbWFpbiIsImVuZXJneSIpXQoKY29sbmFtZXMoQmFyY2Vsb25hX0RhdGEgKVtjb2xuYW1lcyhCYXJjZWxvbmFfRGF0YSApID09ICJlbmVyZ3kiXSA8LSAiZW5lcmd5X2RlbWFuZCIKCmBgYAoKIyMjIFJlbW92YWwgb2Ygcm93cyB3aXRoIGVycm9yZW5vdXMgZGF0YSAKCltSZWZlcmVuY2UgdG8gYXZlcmFnZSB3ZWF0aGVyIGRhdGEgZm9yIEJhcmNlbG9uYV0oaHR0cHM6Ly93ZWF0aGVyc3BhcmsuY29tL3kvNDcyMTMvQXZlcmFnZS1XZWF0aGVyLWluLUJhcmNlbG9uYS1TcGFpbi1ZZWFyLVJvdW5kKQoKCmBgYHtyfQpCYXJjZWxvbmFfRGF0YVtCYXJjZWxvbmFfRGF0YSR0ZW1wIDwgMjcwICxdCkJhcmNlbG9uYV9EYXRhPC0gc3Vic2V0KEJhcmNlbG9uYV9EYXRhLCB0ZW1wPjI3MCkgIyByZW1vdmUgdGVtcGVyYXR1cmUgbGVzcyB0aGFuIDI3MEsKQmFyY2Vsb25hX0RhdGE8LSBzdWJzZXQoQmFyY2Vsb25hX0RhdGEsIHByZXNzdXJlID45MDAgJiBwcmVzc3VyZSA8MTA1MCkgIyByZW1vdmUgcHJlc3N1cmUgb3V0c2lkZSBbOTAwLDEwNTBdbWJhcgpCYXJjZWxvbmFfRGF0YTwtc3Vic2V0KEJhcmNlbG9uYV9EYXRhLCBlbmVyZ3lfZGVtYW5kID4xMCkgIyBSZW1vdmUgYWxsIDAgZGVtYW5kIGZyb20gdGhlIGRhdGEKYGBgCmBgYHtyfQpCYXJjZWxvbmEucmFpbl9kdXJhdGlvbiA8LSBnZ3Bsb3QoQmFyY2Vsb25hX0RhdGEpICsKICAgICAgICAgIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKHJhaW5fZHVyYXRpb24pKSxyYWluX2R1cmF0aW9uKSkgKwogICAgICAgICAgbGFicyhzdWJ0aXRsZT0iUmFpbiBEdXJhdGlvbiBmb3IgQmFyY2Vsb25hIiwgCiAgICAgICAgICB5PSJyYWluIGR1cmF0aW9uIiwgCiAgICAgICAgICB4PSIjIGRhdGEgcG9pbnQiKSAKCkJhcmNlbG9uYS5odW1pZGl0eSA8LSBnZ3Bsb3QoQmFyY2Vsb25hX0RhdGEpICsKICAgICAgICAgIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKGh1bWlkaXR5KSksaHVtaWRpdHkpKSArCiAgICAgICAgICBsYWJzKHN1YnRpdGxlPSJIdW1pZGl0eSBmb3IgQmFyY2Vsb25hIiwgCiAgICAgICAgICB5PSJodW1pZGl0eSIsIAogICAgICAgICAgeD0iIyBkYXRhIHBvaW50IikgIAoKQmFyY2Vsb25hLndpbmRfc3BlZWQgPC0gZ2dwbG90KEJhcmNlbG9uYV9EYXRhKSArCiAgICAgICAgICBnZW9tX3BvaW50KCBhZXMoYygxOmxlbmd0aCh3aW5kX3NwZWVkKSksd2luZF9zcGVlZCkpICsKICAgICAgICAgIGxhYnMoc3VidGl0bGU9IldpbmQgU3BlZWQgZm9yIEJhcmNlbG9uYSIsIAogICAgICAgICAgeT0id2luZF9zcGVlZCIsIAogICAgICAgICAgeD0iIyBkYXRhIHBvaW50IikgIAoKQmFyY2Vsb25hLnByZXNzdXJlIDwtIGdncGxvdChCYXJjZWxvbmFfRGF0YSkgKwogICAgICAgICAgZ2VvbV9wb2ludCggYWVzKGMoMTpsZW5ndGgocHJlc3N1cmUpKSxwcmVzc3VyZSkpICsKICAgICAgICAgIGxhYnMoc3VidGl0bGU9IlByZXNzdXJlIG9mIEJhcmNlbG9uYSIsIAogICAgICAgICAgeT0icHJlc3N1cmUiLCAKICAgICAgICAgIHg9IiMgZGF0YSBwb2ludCIpICAKCmdnYXJyYW5nZShCYXJjZWxvbmEucmFpbl9kdXJhdGlvbixCYXJjZWxvbmEuaHVtaWRpdHksQmFyY2Vsb25hLndpbmRfc3BlZWQsQmFyY2Vsb25hLnByZXNzdXJlLAogICAgICAgICAgbGFiZWxzID0gYygiKEEpIiwgIihCKSIsICIoQykiLCIoRCkiKSwKICAgICAgICAgIG5jb2wgPSAyLCBucm93ID0gMikKCmBgYAoKYGBge3J9CkZ1bGxNb2RlbCA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gQmFyY2Vsb25hX0RhdGEpCnN1bW1hcnkoRnVsbE1vZGVsKSRhZGouci5zcXVhcmVkCmBgYAoKQWRqdXN0ZWQgUjIgb2YgdGhlIG1vZGVsIGlzIHZlcnkgbG93ICowLjMxMjcxNTkqLiAKCldlIHBlcmZvcm0gQXV0b2NvcnJlbGF0aW9uIGFuZCBQYXJ0aWFsIEF1dG9jb3JyZWxhdGlvbiB0byBpbmNvcnBvcmF0ZSB0ZW1wb3JhbCB2YXJpYWJpbGl0eS9pbmZsdWVuY2UgaW4gdGhlIG1vZGVsLiAKCgoKIyMgR2VuZXJhdGlvbiBvZiBBdXRvQ29ycmVsYXRpb24gYW5kIFBhcnRpYWwgQXV0b2NvcnJlbGF0aW9uIHByb2ZpbGUgCmBgYHtyfQoKIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoImFjZnBjZi5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDE0LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwgCiAgIAoKcGFyKG1mcm93ID0gYygxLCAyKSkKQmFyY2Vsb25hLmFjciA8LSBhY2YoQmFyY2Vsb25hX0RhdGEkZW5lcmd5X2RlbWFuZCxwbG90ID0gRkFMU0UpCnBsb3QoQmFyY2Vsb25hLmFjciwgbWFpbiA9ICJFbmVyZ3kgRGVtYW5kIG9mIEJhcmNlbG9uYSIsIHJlcz02MDAsIGNleC5tYWluID0gMykKZ3JpZCgxMCwxMCkKQmFyY2Vsb25hLnBhY3I8LSBwYWNmKEJhcmNlbG9uYV9EYXRhJGVuZXJneV9kZW1hbmQscGxvdCA9RkFMU0UpCnBsb3QoQmFyY2Vsb25hLnBhY3IsIG1haW4gPSAiRW5lcmd5IERlbWFuZCBvZiBCYXJjZWxvbmEiLHJlcz02MDAsIGNleC5tYWluID0gMykKZ3JpZCgxMCwxMCkKCiMgQ2xvc2luZyB0aGUgZ3JhcGhpY2FsIGRldmljZQpkZXYub2ZmKCkgCgoKCgpwYWNmX3ZhbHVlcyA8LWFzLmRhdGEuZnJhbWUoQmFyY2Vsb25hLnBhY3IkYWNmKQoKcGFjZl92YWx1ZXNfZGV0ZWN0PC1zdWJzZXQocGFjZl92YWx1ZXMsIGFicyhwYWNmX3ZhbHVlcykgPjAuMjUpIAoKaGVhZChwYWNmX3ZhbHVlc19kZXRlY3QpCiMgQ2hlY2sgdGhlIGhpZ2hlc3QgcGFydGlhbGx5IGF1dG8gY29ycmVsYXRlZCBlbmVyZ3kgZGVtYW5kIGluIG9yZGVyCmlkeDwtb3JkZXIoYWJzKHBhY2ZfdmFsdWVzX2RldGVjdCRWMSksZGVjcmVhc2luZyA9IFRSVUUpCnBhY2ZfdmFsdWVzX2RldGVjdDwtIHBhY2ZfdmFsdWVzX2RldGVjdFtvcmRlcihhYnMocGFjZl92YWx1ZXNfZGV0ZWN0JFYxKSxkZWNyZWFzaW5nID0gVFJVRSksXQoKYGBgCgoKCiMgQ3JlYXRlIGVuZXJneSBkZW1hbmQgbGFnIHZhcmlhYmxlLCBvbmUgYXQgYSB0aW1lCgpgYGB7cn0KRV8xPC1CYXJjZWxvbmFfRGF0YSRlbmVyZ3lfZGVtYW5kWy0xXSAjIEdldCBhbGwgdGhlIGRhdGEgZXhjZXB0IGluIGZpcnN0IHJvdyAKQmFyY2Vsb25hX0RhdGE8LUJhcmNlbG9uYV9EYXRhWy1ucm93KEJhcmNlbG9uYV9EYXRhKSxdCkJhcmNlbG9uYV9EYXRhJEVfMSA8LSBFXzEKCkVfMjwtRV8xWy0xXQpCYXJjZWxvbmFfRGF0YTwtQmFyY2Vsb25hX0RhdGFbLW5yb3coQmFyY2Vsb25hX0RhdGEpLF0KQmFyY2Vsb25hX0RhdGEkRV8yIDwtIEVfMgoKRV8yNTwtQmFyY2Vsb25hX0RhdGEkZW5lcmd5X2RlbWFuZFsyNTpucm93KEJhcmNlbG9uYV9EYXRhKV0gIyBHZXQgYWxsIHRoZSBkYXRhIGV4Y2VwdCBpbiBmaXJzdCByb3cKQmFyY2Vsb25hX0RhdGE8LUJhcmNlbG9uYV9EYXRhWzE6KG5yb3coQmFyY2Vsb25hX0RhdGEpLTI0KSxdCgpCYXJjZWxvbmFfRGF0YSRFXzI1IDwtIEVfMjUKYGBgCgojIyMgQ2hlY2sgdGhlIG1vZGVsIG5vdwpgYGB7cn0KRnVsbE1vZGVsMSA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IEJhcmNlbG9uYV9EYXRhKQpzdW1tYXJ5KEZ1bGxNb2RlbDEpCgpgYGAKCgoKIyMjIEdlbmVyYXRlIFdvcmtpbmcgYW5kIEV2YWx1YXRpb24gRGF0YQoKYGBge3J9CkJhcmNlbG9uYV9EYXRhIDwtIEJhcmNlbG9uYV9EYXRhIFssIGMoInRpbWVfSUQiLCAiRV8xIiwiRV8yIiwiRV8yNSIsICAgInRlbXAiLCJodW1pZGl0eSIsInByZXNzdXJlIiwid2luZF9zcGVlZCIsInJhaW5fZHVyYXRpb24iLCJzbm93X2R1cmF0aW9uIiwiZGF5X25pZ2h0IiwidGltZV9iYW5kIiwic2Vhc29uIiwid2VhdGhlcl9tYWluIiwiZW5lcmd5X2RlbWFuZCIpXQoKCnNldC5zZWVkKDUwMSkKd29ya2luZ19yb3dzIDwtIHNhbXBsZSgxOm5yb3coQmFyY2Vsb25hX0RhdGEpLDAuODAqbnJvdyhCYXJjZWxvbmFfRGF0YSkpCgp3b3JraW5nX0JhcmNlbG9uYSA8LUJhcmNlbG9uYV9EYXRhW3dvcmtpbmdfcm93cyxdCgp3b3JraW5nX0JhcmNlbG9uYSRpbmRleCA8LSBhcy5udW1lcmljKHJvdy5uYW1lcyh3b3JraW5nX0JhcmNlbG9uYSkpCndvcmtpbmdfQmFyY2Vsb25hPC0gd29ya2luZ19CYXJjZWxvbmFbb3JkZXIod29ya2luZ19CYXJjZWxvbmEkaW5kZXgpLCBdCgp3b3JraW5nX0JhcmNlbG9uYTwtd29ya2luZ19CYXJjZWxvbmEgJT4lCiAgICAgICAgICAgICAgICAgICAgcmVsb2NhdGUoaW5kZXgpIAoKCmV2YWx1YXRpb25fQmFyY2Vsb25hIDwtQmFyY2Vsb25hX0RhdGFbLXdvcmtpbmdfcm93cyxdCmV2YWx1YXRpb25fQmFyY2Vsb25hJGluZGV4IDwtIGFzLm51bWVyaWMocm93Lm5hbWVzKGV2YWx1YXRpb25fQmFyY2Vsb25hKSkKZXZhbHVhdGlvbl9CYXJjZWxvbmE8LSBldmFsdWF0aW9uX0JhcmNlbG9uYVtvcmRlcihldmFsdWF0aW9uX0JhcmNlbG9uYSRpbmRleCksIF0KCmV2YWx1YXRpb25fQmFyY2Vsb25hPC1ldmFsdWF0aW9uX0JhcmNlbG9uYSAlPiUKICAgICAgICAgICAgICAgICAgICByZWxvY2F0ZShpbmRleCkgCgp3cml0ZS5jc3Yod29ya2luZ19CYXJjZWxvbmEsIkRhdGEvd29ya2luZ19CYXJjZWxvbmEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihldmFsdWF0aW9uX0JhcmNlbG9uYSwiRGF0YS9ldmFsdWF0aW9uX0JhcmNlbG9uYS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgoKCgojIyBWYWxlbmNpYSBEQVRBIFByZXBhcmF0aW9uIApgYGB7cn0KClZhbGVuY2lhX3JhdzwtcmVhZC5jc3YoZmlsZSA9ICJEYXRhL1ZhbGVuY2lhLmNzdiIpCmNvbG5hbWVzKFZhbGVuY2lhX3JhdylbY29sbmFtZXMoVmFsZW5jaWFfcmF3KSA9PSAiWCJdIDwtICJEYXRhSUQiCmNvbG5hbWVzKFZhbGVuY2lhX3JhdylbY29sbmFtZXMoVmFsZW5jaWFfcmF3KSA9PSAiZHRfaXNvIl0gPC0gInRpbWUiClZhbGVuY2lhX0RhdGE8LVZhbGVuY2lhX3JhdyAlPiUKICBkcGx5cjo6c2VsZWN0KERhdGFJRCx0aW1lLHRlbXAsaHVtaWRpdHkscHJlc3N1cmUsd2luZF9zcGVlZCxyYWluXzFoLHJhaW5fM2gsc25vd18zaCx3ZWF0aGVyX21haW4sZW5lcmd5KQoKVmFsZW5jaWFfRGF0YTwtdHJhbnNmb3JtKFZhbGVuY2lhX0RhdGEsIHdlYXRoZXJfbWFpbiA9IGZhY3Rvcih3ZWF0aGVyX21haW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoImNsZWFyIiwgImNsb3VkcyIsICJkcml6emxlIiwiZHVzdCIsImZvZyIsImhhemUiLCJtaXN0IiwicmFpbiIsInNtb2tlIiwic25vdyIsInNxdWFsbCIsInRodW5kZXJzdG9ybSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoMToxMikpKQoKIyBBZGQgdHdvIGNvbHVtbnMgb2YgcmFpbiBkdXJhdGlvbgpWYWxlbmNpYV9EYXRhWyJyYWluX2R1cmF0aW9uIl0gPC0gVmFsZW5jaWFfRGF0YSRyYWluXzFoICsgVmFsZW5jaWFfRGF0YSRyYWluXzNoIAoKIyBSZW5hbWUgQ29sdW1uIE5hbWVzIGZvciBjbGFyaXR5IApWYWxlbmNpYV9EYXRhIDwtbWVyZ2UoVmFsZW5jaWFfRGF0YSwgZW5lcmd5X3Jhd1ssYygidGltZSIsIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBCYW5kIChQZWFrLCBPZmYtUGVhaywgTWlkLVBlYWspIildLCBieS54ID0gInRpbWUiLCBieS55ID0gInRpbWUiKQpjb2xuYW1lcyhWYWxlbmNpYV9EYXRhIClbY29sbmFtZXMoVmFsZW5jaWFfRGF0YSApID09ICJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblRpbWUgQmFuZCAoUGVhaywgT2ZmLVBlYWssIE1pZC1QZWFrKSJdIDwtICJ0aW1lX2JhbmQiCgpWYWxlbmNpYV9EYXRhIDwtbWVyZ2UoVmFsZW5jaWFfRGF0YSwgZW5lcmd5X3Jhd1ssYygidGltZSIsIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuU2Vhc29uIChTcHJpbmcsIFN1bW1lciwgQXV0dW1uLCBXaW50ZXIpIildLCBieS54ID0gInRpbWUiLCBieS55ID0gInRpbWUiKQpjb2xuYW1lcyhWYWxlbmNpYV9EYXRhIClbY29sbmFtZXMoVmFsZW5jaWFfRGF0YSApID09ICJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblNlYXNvbiAoU3ByaW5nLCBTdW1tZXIsIEF1dHVtbiwgV2ludGVyKSJdIDwtICJzZWFzb24iCgpWYWxlbmNpYV9EYXRhIDwtbWVyZ2UoVmFsZW5jaWFfRGF0YSwgZW5lcmd5X3Jhd1ssYygidGltZSIsIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBvZiBkYXkgKERheSB2cyBOaWdodCkiKV0sIGJ5LnggPSAidGltZSIsIGJ5LnkgPSAidGltZSIpCmNvbG5hbWVzKFZhbGVuY2lhX0RhdGEgKVtjb2xuYW1lcyhWYWxlbmNpYV9EYXRhICkgPT0gIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBvZiBkYXkgKERheSB2cyBOaWdodCkiXSA8LSAiZGF5X25pZ2h0IgoKCmNvbG5hbWVzKFZhbGVuY2lhX0RhdGEgKVtjb2xuYW1lcyhWYWxlbmNpYV9EYXRhICkgPT0gInNub3dfM2giXSA8LSAic25vd19kdXJhdGlvbiIKClZhbGVuY2lhX0RhdGE8LXN1YnNldChWYWxlbmNpYV9EYXRhLHNlbGVjdD0tYyhyYWluXzFoLHJhaW5fM2gsRGF0YUlEKSkKCmNvbG5hbWVzKFZhbGVuY2lhX0RhdGEgKVtjb2xuYW1lcyhWYWxlbmNpYV9EYXRhICkgPT0gInRpbWUiXSA8LSAidGltZV9JRCIKClZhbGVuY2lhX0RhdGEgPC0gVmFsZW5jaWFfRGF0YSBbLCBjKCJ0aW1lX0lEIiwidGVtcCIsImh1bWlkaXR5IiwicHJlc3N1cmUiLCJ3aW5kX3NwZWVkIiwicmFpbl9kdXJhdGlvbiIsInNub3dfZHVyYXRpb24iLCJkYXlfbmlnaHQiLCJ0aW1lX2JhbmQiLCJzZWFzb24iLCJ3ZWF0aGVyX21haW4iLCJlbmVyZ3kiKV0KCmNvbG5hbWVzKFZhbGVuY2lhX0RhdGEgKVtjb2xuYW1lcyhWYWxlbmNpYV9EYXRhICkgPT0gImVuZXJneSJdIDwtICJlbmVyZ3lfZGVtYW5kIgoKVkRfPC0gVmFsZW5jaWFfRGF0YVtWYWxlbmNpYV9EYXRhJHRlbXAgPCAyNzAgLF0KVmFsZW5jaWFfRGF0YTwtIHN1YnNldChWYWxlbmNpYV9EYXRhLCB0ZW1wPjI3MCkgIyByZW1vdmUgdGVtcGVyYXR1cmUgbGVzcyB0aGFuIDI3MEsKVmFsZW5jaWFfRGF0YTwtIHN1YnNldChWYWxlbmNpYV9EYXRhLCBwcmVzc3VyZSA+OTAwICYgcHJlc3N1cmUgPDEwNTApICMgcmVtb3ZlIHByZXNzdXJlIG91dHNpZGUgWzkwMCwxMDUwXW1iYXIKVmFsZW5jaWFfRGF0YTwtc3Vic2V0KFZhbGVuY2lhX0RhdGEsIGVuZXJneV9kZW1hbmQgPjEwKSAjIFJlbW92ZSBhbGwgMCBkZW1hbmQgZnJvbSB0aGUgZGF0YQoKVmFsZW5jaWEucmFpbl9kdXJhdGlvbiA8LSBnZ3Bsb3QoVmFsZW5jaWFfRGF0YSkgKwogIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKHJhaW5fZHVyYXRpb24pKSxyYWluX2R1cmF0aW9uKSkgKwogIGxhYnMoc3VidGl0bGU9IlJhaW4gRHVyYXRpb24gZm9yIFZhbGVuY2lhIiwgCiAgICAgICB5PSJyYWluIGR1cmF0aW9uIiwgCiAgICAgICB4PSIjIGRhdGEgcG9pbnQiKSAKClZhbGVuY2lhLmh1bWlkaXR5IDwtIGdncGxvdChWYWxlbmNpYV9EYXRhKSArCiAgZ2VvbV9wb2ludCggYWVzKGMoMTpsZW5ndGgoaHVtaWRpdHkpKSxodW1pZGl0eSkpICsKICBsYWJzKHN1YnRpdGxlPSJIdW1pZGl0eSBmb3IgVmFsZW5jaWEiLCAKICAgICAgIHk9Imh1bWlkaXR5IiwgCiAgICAgICB4PSIjIGRhdGEgcG9pbnQiKSAgCgpWYWxlbmNpYS53aW5kX3NwZWVkIDwtIGdncGxvdChWYWxlbmNpYV9EYXRhKSArCiAgZ2VvbV9wb2ludCggYWVzKGMoMTpsZW5ndGgod2luZF9zcGVlZCkpLHdpbmRfc3BlZWQpKSArCiAgbGFicyhzdWJ0aXRsZT0iV2luZCBTcGVlZCBmb3IgVmFsZW5jaWEiLCAKICAgICAgIHk9IndpbmRfc3BlZWQiLCAKICAgICAgIHg9IiMgZGF0YSBwb2ludCIpICAKClZhbGVuY2lhLnByZXNzdXJlIDwtIGdncGxvdChWYWxlbmNpYV9EYXRhKSArCiAgZ2VvbV9wb2ludCggYWVzKGMoMTpsZW5ndGgocHJlc3N1cmUpKSxwcmVzc3VyZSkpICsKICBsYWJzKHN1YnRpdGxlPSJQcmVzc3VyZSBvZiBWYWxlbmNpYSIsIAogICAgICAgeT0icHJlc3N1cmUiLCAKICAgICAgIHg9IiMgZGF0YSBwb2ludCIpICAKCmdnYXJyYW5nZShWYWxlbmNpYS5yYWluX2R1cmF0aW9uLFZhbGVuY2lhLmh1bWlkaXR5LFZhbGVuY2lhLndpbmRfc3BlZWQsVmFsZW5jaWEucHJlc3N1cmUsCiAgICAgICAgICBsYWJlbHMgPSBjKCIoQSkiLCAiKEIpIiwgIihDKSIsIihEKSIpLAogICAgICAgICAgbmNvbCA9IDIsIG5yb3cgPSAyKQoKCkVfMTwtVmFsZW5jaWFfRGF0YSRlbmVyZ3lfZGVtYW5kWy0xXSAjIEdldCBhbGwgdGhlIGRhdGEgZXhjZXB0IGluIGZpcnN0IHJvdyAKVmFsZW5jaWFfRGF0YTwtVmFsZW5jaWFfRGF0YVstbnJvdyhWYWxlbmNpYV9EYXRhKSxdClZhbGVuY2lhX0RhdGEkRV8xIDwtIEVfMQoKRV8yPC1FXzFbLTFdClZhbGVuY2lhX0RhdGE8LVZhbGVuY2lhX0RhdGFbLW5yb3coVmFsZW5jaWFfRGF0YSksXQpWYWxlbmNpYV9EYXRhJEVfMiA8LSBFXzIKCkVfMjU8LVZhbGVuY2lhX0RhdGEkZW5lcmd5X2RlbWFuZFsyNTpucm93KFZhbGVuY2lhX0RhdGEpXSAjIEdldCBhbGwgdGhlIGRhdGEgZXhjZXB0IGluIGZpcnN0IHJvdwpWYWxlbmNpYV9EYXRhPC1WYWxlbmNpYV9EYXRhWzE6KG5yb3coVmFsZW5jaWFfRGF0YSktMjQpLF0KClZhbGVuY2lhX0RhdGEkRV8yNSA8LSBFXzI1CgpWYWxlbmNpYV9EYXRhIDwtIFZhbGVuY2lhX0RhdGEgWywgYygidGltZV9JRCIsICJFXzEiLCJFXzIiLCJFXzI1IiwgICAidGVtcCIsImh1bWlkaXR5IiwicHJlc3N1cmUiLCJ3aW5kX3NwZWVkIiwicmFpbl9kdXJhdGlvbiIsInNub3dfZHVyYXRpb24iLCJkYXlfbmlnaHQiLCJ0aW1lX2JhbmQiLCJzZWFzb24iLCJ3ZWF0aGVyX21haW4iLCJlbmVyZ3lfZGVtYW5kIildCgoKIyMjIEdlbmVyYXRlIFdvcmtpbmcgYW5kIEV2YWx1YXRpb24gRGF0YQpzZXQuc2VlZCg1MDEpCndvcmtpbmdfcm93cyA8LSBzYW1wbGUoMTpucm93KFZhbGVuY2lhX0RhdGEpLDAuODAqbnJvdyhWYWxlbmNpYV9EYXRhKSkKCndvcmtpbmdfVmFsZW5jaWEgPC1WYWxlbmNpYV9EYXRhW3dvcmtpbmdfcm93cyxdCgp3b3JraW5nX1ZhbGVuY2lhJGluZGV4IDwtIGFzLm51bWVyaWMocm93Lm5hbWVzKHdvcmtpbmdfVmFsZW5jaWEpKQp3b3JraW5nX1ZhbGVuY2lhPC0gd29ya2luZ19WYWxlbmNpYVtvcmRlcih3b3JraW5nX1ZhbGVuY2lhJGluZGV4KSwgXQoKd29ya2luZ19WYWxlbmNpYTwtd29ya2luZ19WYWxlbmNpYSAlPiUKICByZWxvY2F0ZShpbmRleCkgCgoKZXZhbHVhdGlvbl9WYWxlbmNpYSA8LVZhbGVuY2lhX0RhdGFbLXdvcmtpbmdfcm93cyxdCmV2YWx1YXRpb25fVmFsZW5jaWEkaW5kZXggPC0gYXMubnVtZXJpYyhyb3cubmFtZXMoZXZhbHVhdGlvbl9WYWxlbmNpYSkpCmV2YWx1YXRpb25fVmFsZW5jaWE8LSBldmFsdWF0aW9uX1ZhbGVuY2lhW29yZGVyKGV2YWx1YXRpb25fVmFsZW5jaWEkaW5kZXgpLCBdCgpldmFsdWF0aW9uX1ZhbGVuY2lhPC1ldmFsdWF0aW9uX1ZhbGVuY2lhICU+JQogIHJlbG9jYXRlKGluZGV4KSAKCndyaXRlLmNzdih3b3JraW5nX1ZhbGVuY2lhLCJEYXRhL3dvcmtpbmdfVmFsZW5jaWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihldmFsdWF0aW9uX1ZhbGVuY2lhLCJEYXRhL2V2YWx1YXRpb25fVmFsZW5jaWEuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCgpgYGAKCgojIyBCaWxiYW8gREFUQSBQcmVwYXJhdGlvbiAKYGBge3J9CgpCaWxiYW9fcmF3PC1yZWFkLmNzdihmaWxlID0gIkRhdGEvQmlsYmFvLmNzdiIpCmNvbG5hbWVzKEJpbGJhb19yYXcpW2NvbG5hbWVzKEJpbGJhb19yYXcpID09ICJYIl0gPC0gIkRhdGFJRCIKY29sbmFtZXMoQmlsYmFvX3JhdylbY29sbmFtZXMoQmlsYmFvX3JhdykgPT0gImR0X2lzbyJdIDwtICJ0aW1lIgpCaWxiYW9fRGF0YTwtQmlsYmFvX3JhdyAlPiUKICBkcGx5cjo6c2VsZWN0KERhdGFJRCx0aW1lLHRlbXAsaHVtaWRpdHkscHJlc3N1cmUsd2luZF9zcGVlZCxyYWluXzFoLHJhaW5fM2gsc25vd18zaCx3ZWF0aGVyX21haW4sZW5lcmd5KQoKQmlsYmFvX0RhdGE8LXRyYW5zZm9ybShCaWxiYW9fRGF0YSwgd2VhdGhlcl9tYWluID0gZmFjdG9yKHdlYXRoZXJfbWFpbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiY2xlYXIiLCAiY2xvdWRzIiwgImRyaXp6bGUiLCJkdXN0IiwiZm9nIiwiaGF6ZSIsIm1pc3QiLCJyYWluIiwic21va2UiLCJzbm93Iiwic3F1YWxsIiwidGh1bmRlcnN0b3JtIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygxOjEyKSkpCgojIEFkZCB0d28gY29sdW1ucyBvZiByYWluIGR1cmF0aW9uCkJpbGJhb19EYXRhWyJyYWluX2R1cmF0aW9uIl0gPC0gQmlsYmFvX0RhdGEkcmFpbl8xaCArIEJpbGJhb19EYXRhJHJhaW5fM2ggCgojIFJlbmFtZSBDb2x1bW4gTmFtZXMgZm9yIGNsYXJpdHkgCkJpbGJhb19EYXRhIDwtbWVyZ2UoQmlsYmFvX0RhdGEsIGVuZXJneV9yYXdbLGMoInRpbWUiLCJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblRpbWUgQmFuZCAoUGVhaywgT2ZmLVBlYWssIE1pZC1QZWFrKSIpXSwgYnkueCA9ICJ0aW1lIiwgYnkueSA9ICJ0aW1lIikKY29sbmFtZXMoQmlsYmFvX0RhdGEgKVtjb2xuYW1lcyhCaWxiYW9fRGF0YSApID09ICJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblRpbWUgQmFuZCAoUGVhaywgT2ZmLVBlYWssIE1pZC1QZWFrKSJdIDwtICJ0aW1lX2JhbmQiCgpCaWxiYW9fRGF0YSA8LW1lcmdlKEJpbGJhb19EYXRhLCBlbmVyZ3lfcmF3WyxjKCJ0aW1lIiwiU3BlY2lmaWVkIENhdGVnb3JpY2FsIFZhcmlhYmxlOlxyXG5TZWFzb24gKFNwcmluZywgU3VtbWVyLCBBdXR1bW4sIFdpbnRlcikiKV0sIGJ5LnggPSAidGltZSIsIGJ5LnkgPSAidGltZSIpCmNvbG5hbWVzKEJpbGJhb19EYXRhIClbY29sbmFtZXMoQmlsYmFvX0RhdGEgKSA9PSAiU3BlY2lmaWVkIENhdGVnb3JpY2FsIFZhcmlhYmxlOlxyXG5TZWFzb24gKFNwcmluZywgU3VtbWVyLCBBdXR1bW4sIFdpbnRlcikiXSA8LSAic2Vhc29uIgoKQmlsYmFvX0RhdGEgPC1tZXJnZShCaWxiYW9fRGF0YSwgZW5lcmd5X3Jhd1ssYygidGltZSIsIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBvZiBkYXkgKERheSB2cyBOaWdodCkiKV0sIGJ5LnggPSAidGltZSIsIGJ5LnkgPSAidGltZSIpCmNvbG5hbWVzKEJpbGJhb19EYXRhIClbY29sbmFtZXMoQmlsYmFvX0RhdGEgKSA9PSAiU3BlY2lmaWVkIENhdGVnb3JpY2FsIFZhcmlhYmxlOlxyXG5UaW1lIG9mIGRheSAoRGF5IHZzIE5pZ2h0KSJdIDwtICJkYXlfbmlnaHQiCgoKY29sbmFtZXMoQmlsYmFvX0RhdGEgKVtjb2xuYW1lcyhCaWxiYW9fRGF0YSApID09ICJzbm93XzNoIl0gPC0gInNub3dfZHVyYXRpb24iCgpCaWxiYW9fRGF0YTwtc3Vic2V0KEJpbGJhb19EYXRhLHNlbGVjdD0tYyhyYWluXzFoLHJhaW5fM2gsRGF0YUlEKSkKCmNvbG5hbWVzKEJpbGJhb19EYXRhIClbY29sbmFtZXMoQmlsYmFvX0RhdGEgKSA9PSAidGltZSJdIDwtICJ0aW1lX0lEIgoKQmlsYmFvX0RhdGEgPC0gQmlsYmFvX0RhdGEgWywgYygidGltZV9JRCIsInRlbXAiLCJodW1pZGl0eSIsInByZXNzdXJlIiwid2luZF9zcGVlZCIsInJhaW5fZHVyYXRpb24iLCJzbm93X2R1cmF0aW9uIiwiZGF5X25pZ2h0IiwidGltZV9iYW5kIiwic2Vhc29uIiwid2VhdGhlcl9tYWluIiwiZW5lcmd5IildCgpjb2xuYW1lcyhCaWxiYW9fRGF0YSApW2NvbG5hbWVzKEJpbGJhb19EYXRhICkgPT0gImVuZXJneSJdIDwtICJlbmVyZ3lfZGVtYW5kIgoKQkRfPC0gQmlsYmFvX0RhdGFbQmlsYmFvX0RhdGEkdGVtcCA8IDI3MCAsXQpCaWxiYW9fRGF0YTwtIHN1YnNldChCaWxiYW9fRGF0YSwgdGVtcD4yNzApICMgcmVtb3ZlIHRlbXBlcmF0dXJlIGxlc3MgdGhhbiAyNzBLCkJpbGJhb19EYXRhPC0gc3Vic2V0KEJpbGJhb19EYXRhLCBwcmVzc3VyZSA+OTAwICYgcHJlc3N1cmUgPDEwNTApICMgcmVtb3ZlIHByZXNzdXJlIG91dHNpZGUgWzkwMCwxMDUwXW1iYXIKQmlsYmFvX0RhdGE8LXN1YnNldChCaWxiYW9fRGF0YSwgZW5lcmd5X2RlbWFuZCA+MTApICMgUmVtb3ZlIGFsbCAwIGRlbWFuZCBmcm9tIHRoZSBkYXRhCgpCaWxiYW8ucmFpbl9kdXJhdGlvbiA8LSBnZ3Bsb3QoQmlsYmFvX0RhdGEpICsKICBnZW9tX3BvaW50KCBhZXMoYygxOmxlbmd0aChyYWluX2R1cmF0aW9uKSkscmFpbl9kdXJhdGlvbikpICsKICBsYWJzKHN1YnRpdGxlPSJSYWluIER1cmF0aW9uIGZvciBCaWxiYW8iLCAKICAgICAgIHk9InJhaW4gZHVyYXRpb24iLCAKICAgICAgIHg9IiMgZGF0YSBwb2ludCIpIAoKQmlsYmFvLmh1bWlkaXR5IDwtIGdncGxvdChCaWxiYW9fRGF0YSkgKwogIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKGh1bWlkaXR5KSksaHVtaWRpdHkpKSArCiAgbGFicyhzdWJ0aXRsZT0iSHVtaWRpdHkgZm9yIEJpbGJhbyIsIAogICAgICAgeT0iaHVtaWRpdHkiLCAKICAgICAgIHg9IiMgZGF0YSBwb2ludCIpICAKCkJpbGJhby53aW5kX3NwZWVkIDwtIGdncGxvdChCaWxiYW9fRGF0YSkgKwogIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKHdpbmRfc3BlZWQpKSx3aW5kX3NwZWVkKSkgKwogIGxhYnMoc3VidGl0bGU9IldpbmQgU3BlZWQgZm9yIEJpbGJhbyIsIAogICAgICAgeT0id2luZF9zcGVlZCIsIAogICAgICAgeD0iIyBkYXRhIHBvaW50IikgIAoKQmlsYmFvLnByZXNzdXJlIDwtIGdncGxvdChCaWxiYW9fRGF0YSkgKwogIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKHByZXNzdXJlKSkscHJlc3N1cmUpKSArCiAgbGFicyhzdWJ0aXRsZT0iUHJlc3N1cmUgb2YgQmlsYmFvIiwgCiAgICAgICB5PSJwcmVzc3VyZSIsIAogICAgICAgeD0iIyBkYXRhIHBvaW50IikgIAoKZ2dhcnJhbmdlKEJpbGJhby5yYWluX2R1cmF0aW9uLEJpbGJhby5odW1pZGl0eSxCaWxiYW8ud2luZF9zcGVlZCxCaWxiYW8ucHJlc3N1cmUsCiAgICAgICAgICBsYWJlbHMgPSBjKCIoQSkiLCAiKEIpIiwgIihDKSIsIihEKSIpLAogICAgICAgICAgbmNvbCA9IDIsIG5yb3cgPSAyKQoKCkVfMTwtQmlsYmFvX0RhdGEkZW5lcmd5X2RlbWFuZFstMV0gIyBHZXQgYWxsIHRoZSBkYXRhIGV4Y2VwdCBpbiBmaXJzdCByb3cgCkJpbGJhb19EYXRhPC1CaWxiYW9fRGF0YVstbnJvdyhCaWxiYW9fRGF0YSksXQpCaWxiYW9fRGF0YSRFXzEgPC0gRV8xCgpFXzI8LUVfMVstMV0KQmlsYmFvX0RhdGE8LUJpbGJhb19EYXRhWy1ucm93KEJpbGJhb19EYXRhKSxdCkJpbGJhb19EYXRhJEVfMiA8LSBFXzIKCkVfMjU8LUJpbGJhb19EYXRhJGVuZXJneV9kZW1hbmRbMjU6bnJvdyhCaWxiYW9fRGF0YSldICMgR2V0IGFsbCB0aGUgZGF0YSBleGNlcHQgaW4gZmlyc3Qgcm93CkJpbGJhb19EYXRhPC1CaWxiYW9fRGF0YVsxOihucm93KEJpbGJhb19EYXRhKS0yNCksXQoKQmlsYmFvX0RhdGEkRV8yNSA8LSBFXzI1CgpCaWxiYW9fRGF0YSA8LSBCaWxiYW9fRGF0YSBbLCBjKCJ0aW1lX0lEIiwgIkVfMSIsIkVfMiIsIkVfMjUiLCAgICJ0ZW1wIiwiaHVtaWRpdHkiLCJwcmVzc3VyZSIsIndpbmRfc3BlZWQiLCJyYWluX2R1cmF0aW9uIiwic25vd19kdXJhdGlvbiIsImRheV9uaWdodCIsInRpbWVfYmFuZCIsInNlYXNvbiIsIndlYXRoZXJfbWFpbiIsImVuZXJneV9kZW1hbmQiKV0KCgojIyMgR2VuZXJhdGUgV29ya2luZyBhbmQgRXZhbHVhdGlvbiBEYXRhCnNldC5zZWVkKDUwMSkKd29ya2luZ19yb3dzIDwtIHNhbXBsZSgxOm5yb3coQmlsYmFvX0RhdGEpLDAuODAqbnJvdyhCaWxiYW9fRGF0YSkpCgp3b3JraW5nX0JpbGJhbyA8LUJpbGJhb19EYXRhW3dvcmtpbmdfcm93cyxdCgp3b3JraW5nX0JpbGJhbyRpbmRleCA8LSBhcy5udW1lcmljKHJvdy5uYW1lcyh3b3JraW5nX0JpbGJhbykpCndvcmtpbmdfQmlsYmFvPC0gd29ya2luZ19CaWxiYW9bb3JkZXIod29ya2luZ19CaWxiYW8kaW5kZXgpLCBdCgp3b3JraW5nX0JpbGJhbzwtd29ya2luZ19CaWxiYW8gJT4lCiAgcmVsb2NhdGUoaW5kZXgpIAoKCmV2YWx1YXRpb25fQmlsYmFvIDwtQmlsYmFvX0RhdGFbLXdvcmtpbmdfcm93cyxdCmV2YWx1YXRpb25fQmlsYmFvJGluZGV4IDwtIGFzLm51bWVyaWMocm93Lm5hbWVzKGV2YWx1YXRpb25fQmlsYmFvKSkKZXZhbHVhdGlvbl9CaWxiYW88LSBldmFsdWF0aW9uX0JpbGJhb1tvcmRlcihldmFsdWF0aW9uX0JpbGJhbyRpbmRleCksIF0KCmV2YWx1YXRpb25fQmlsYmFvPC1ldmFsdWF0aW9uX0JpbGJhbyAlPiUKICByZWxvY2F0ZShpbmRleCkgCgp3cml0ZS5jc3Yod29ya2luZ19CaWxiYW8sIkRhdGEvd29ya2luZ19CaWxiYW8uY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihldmFsdWF0aW9uX0JpbGJhbywiRGF0YS9ldmFsdWF0aW9uX0JpbGJhby5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKCiMjIFNldmlsbGUgREFUQSBQcmVwYXJhdGlvbiAKYGBge3J9CgpTZXZpbGxlX3JhdzwtcmVhZC5jc3YoZmlsZSA9ICJEYXRhL1NldmlsbGUuY3N2IikKY29sbmFtZXMoU2V2aWxsZV9yYXcpW2NvbG5hbWVzKFNldmlsbGVfcmF3KSA9PSAiWCJdIDwtICJEYXRhSUQiCmNvbG5hbWVzKFNldmlsbGVfcmF3KVtjb2xuYW1lcyhTZXZpbGxlX3JhdykgPT0gImR0X2lzbyJdIDwtICJ0aW1lIgpTZXZpbGxlX0RhdGE8LVNldmlsbGVfcmF3ICU+JQogIGRwbHlyOjpzZWxlY3QoRGF0YUlELHRpbWUsdGVtcCxodW1pZGl0eSxwcmVzc3VyZSx3aW5kX3NwZWVkLHJhaW5fMWgscmFpbl8zaCxzbm93XzNoLHdlYXRoZXJfbWFpbixlbmVyZ3kpCgpTZXZpbGxlX0RhdGE8LXRyYW5zZm9ybShTZXZpbGxlX0RhdGEsIHdlYXRoZXJfbWFpbiA9IGZhY3Rvcih3ZWF0aGVyX21haW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoImNsZWFyIiwgImNsb3VkcyIsICJkcml6emxlIiwiZHVzdCIsImZvZyIsImhhemUiLCJtaXN0IiwicmFpbiIsInNtb2tlIiwic25vdyIsInNxdWFsbCIsInRodW5kZXJzdG9ybSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoMToxMikpKQoKIyBBZGQgdHdvIGNvbHVtbnMgb2YgcmFpbiBkdXJhdGlvbgpTZXZpbGxlX0RhdGFbInJhaW5fZHVyYXRpb24iXSA8LSBTZXZpbGxlX0RhdGEkcmFpbl8xaCArIFNldmlsbGVfRGF0YSRyYWluXzNoIAoKIyBSZW5hbWUgQ29sdW1uIE5hbWVzIGZvciBjbGFyaXR5IApTZXZpbGxlX0RhdGEgPC1tZXJnZShTZXZpbGxlX0RhdGEsIGVuZXJneV9yYXdbLGMoInRpbWUiLCJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblRpbWUgQmFuZCAoUGVhaywgT2ZmLVBlYWssIE1pZC1QZWFrKSIpXSwgYnkueCA9ICJ0aW1lIiwgYnkueSA9ICJ0aW1lIikKY29sbmFtZXMoU2V2aWxsZV9EYXRhIClbY29sbmFtZXMoU2V2aWxsZV9EYXRhICkgPT0gIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBCYW5kIChQZWFrLCBPZmYtUGVhaywgTWlkLVBlYWspIl0gPC0gInRpbWVfYmFuZCIKClNldmlsbGVfRGF0YSA8LW1lcmdlKFNldmlsbGVfRGF0YSwgZW5lcmd5X3Jhd1ssYygidGltZSIsIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuU2Vhc29uIChTcHJpbmcsIFN1bW1lciwgQXV0dW1uLCBXaW50ZXIpIildLCBieS54ID0gInRpbWUiLCBieS55ID0gInRpbWUiKQpjb2xuYW1lcyhTZXZpbGxlX0RhdGEgKVtjb2xuYW1lcyhTZXZpbGxlX0RhdGEgKSA9PSAiU3BlY2lmaWVkIENhdGVnb3JpY2FsIFZhcmlhYmxlOlxyXG5TZWFzb24gKFNwcmluZywgU3VtbWVyLCBBdXR1bW4sIFdpbnRlcikiXSA8LSAic2Vhc29uIgoKU2V2aWxsZV9EYXRhIDwtbWVyZ2UoU2V2aWxsZV9EYXRhLCBlbmVyZ3lfcmF3WyxjKCJ0aW1lIiwiU3BlY2lmaWVkIENhdGVnb3JpY2FsIFZhcmlhYmxlOlxyXG5UaW1lIG9mIGRheSAoRGF5IHZzIE5pZ2h0KSIpXSwgYnkueCA9ICJ0aW1lIiwgYnkueSA9ICJ0aW1lIikKY29sbmFtZXMoU2V2aWxsZV9EYXRhIClbY29sbmFtZXMoU2V2aWxsZV9EYXRhICkgPT0gIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBvZiBkYXkgKERheSB2cyBOaWdodCkiXSA8LSAiZGF5X25pZ2h0IgoKCmNvbG5hbWVzKFNldmlsbGVfRGF0YSApW2NvbG5hbWVzKFNldmlsbGVfRGF0YSApID09ICJzbm93XzNoIl0gPC0gInNub3dfZHVyYXRpb24iCgpTZXZpbGxlX0RhdGE8LXN1YnNldChTZXZpbGxlX0RhdGEsc2VsZWN0PS1jKHJhaW5fMWgscmFpbl8zaCxEYXRhSUQpKQoKY29sbmFtZXMoU2V2aWxsZV9EYXRhIClbY29sbmFtZXMoU2V2aWxsZV9EYXRhICkgPT0gInRpbWUiXSA8LSAidGltZV9JRCIKClNldmlsbGVfRGF0YSA8LSBTZXZpbGxlX0RhdGEgWywgYygidGltZV9JRCIsInRlbXAiLCJodW1pZGl0eSIsInByZXNzdXJlIiwid2luZF9zcGVlZCIsInJhaW5fZHVyYXRpb24iLCJzbm93X2R1cmF0aW9uIiwiZGF5X25pZ2h0IiwidGltZV9iYW5kIiwic2Vhc29uIiwid2VhdGhlcl9tYWluIiwiZW5lcmd5IildCgpjb2xuYW1lcyhTZXZpbGxlX0RhdGEgKVtjb2xuYW1lcyhTZXZpbGxlX0RhdGEgKSA9PSAiZW5lcmd5Il0gPC0gImVuZXJneV9kZW1hbmQiCgpTRF88LVNldmlsbGVfRGF0YVtTZXZpbGxlX0RhdGEkdGVtcCA8IDI3MCAsXQpTZXZpbGxlX0RhdGE8LSBzdWJzZXQoU2V2aWxsZV9EYXRhLCB0ZW1wPjI3MCkgIyByZW1vdmUgdGVtcGVyYXR1cmUgbGVzcyB0aGFuIDI3MEsKU2V2aWxsZV9EYXRhPC0gc3Vic2V0KFNldmlsbGVfRGF0YSwgcHJlc3N1cmUgPjkwMCAmIHByZXNzdXJlIDwxMDUwKSAjIHJlbW92ZSBwcmVzc3VyZSBvdXRzaWRlIFs5MDAsMTA1MF1tYmFyClNldmlsbGVfRGF0YTwtc3Vic2V0KFNldmlsbGVfRGF0YSwgZW5lcmd5X2RlbWFuZCA+MTApICMgUmVtb3ZlIGFsbCAwIGRlbWFuZCBmcm9tIHRoZSBkYXRhCgpTZXZpbGxlLnJhaW5fZHVyYXRpb24gPC0gZ2dwbG90KFNldmlsbGVfRGF0YSkgKwogIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKHJhaW5fZHVyYXRpb24pKSxyYWluX2R1cmF0aW9uKSkgKwogIGxhYnMoc3VidGl0bGU9IlJhaW4gRHVyYXRpb24gZm9yIFNldmlsbGUiLCAKICAgICAgIHk9InJhaW4gZHVyYXRpb24iLCAKICAgICAgIHg9IiMgZGF0YSBwb2ludCIpIAoKU2V2aWxsZS5odW1pZGl0eSA8LSBnZ3Bsb3QoU2V2aWxsZV9EYXRhKSArCiAgZ2VvbV9wb2ludCggYWVzKGMoMTpsZW5ndGgoaHVtaWRpdHkpKSxodW1pZGl0eSkpICsKICBsYWJzKHN1YnRpdGxlPSJIdW1pZGl0eSBmb3IgU2V2aWxsZSIsIAogICAgICAgeT0iaHVtaWRpdHkiLCAKICAgICAgIHg9IiMgZGF0YSBwb2ludCIpICAKClNldmlsbGUud2luZF9zcGVlZCA8LSBnZ3Bsb3QoU2V2aWxsZV9EYXRhKSArCiAgZ2VvbV9wb2ludCggYWVzKGMoMTpsZW5ndGgod2luZF9zcGVlZCkpLHdpbmRfc3BlZWQpKSArCiAgbGFicyhzdWJ0aXRsZT0iV2luZCBTcGVlZCBmb3IgU2V2aWxsZSIsIAogICAgICAgeT0id2luZF9zcGVlZCIsIAogICAgICAgeD0iIyBkYXRhIHBvaW50IikgIAoKU2V2aWxsZS5wcmVzc3VyZSA8LSBnZ3Bsb3QoU2V2aWxsZV9EYXRhKSArCiAgZ2VvbV9wb2ludCggYWVzKGMoMTpsZW5ndGgocHJlc3N1cmUpKSxwcmVzc3VyZSkpICsKICBsYWJzKHN1YnRpdGxlPSJQcmVzc3VyZSBvZiBTZXZpbGxlIiwgCiAgICAgICB5PSJwcmVzc3VyZSIsIAogICAgICAgeD0iIyBkYXRhIHBvaW50IikgIAoKZ2dhcnJhbmdlKFNldmlsbGUucmFpbl9kdXJhdGlvbixTZXZpbGxlLmh1bWlkaXR5LFNldmlsbGUud2luZF9zcGVlZCxTZXZpbGxlLnByZXNzdXJlLAogICAgICAgICAgbGFiZWxzID0gYygiKEEpIiwgIihCKSIsICIoQykiLCIoRCkiKSwKICAgICAgICAgIG5jb2wgPSAyLCBucm93ID0gMikKCgpFXzE8LVNldmlsbGVfRGF0YSRlbmVyZ3lfZGVtYW5kWy0xXSAjIEdldCBhbGwgdGhlIGRhdGEgZXhjZXB0IGluIGZpcnN0IHJvdyAKU2V2aWxsZV9EYXRhPC1TZXZpbGxlX0RhdGFbLW5yb3coU2V2aWxsZV9EYXRhKSxdClNldmlsbGVfRGF0YSRFXzEgPC0gRV8xCgpFXzI8LUVfMVstMV0KU2V2aWxsZV9EYXRhPC1TZXZpbGxlX0RhdGFbLW5yb3coU2V2aWxsZV9EYXRhKSxdClNldmlsbGVfRGF0YSRFXzIgPC0gRV8yCgpFXzI1PC1TZXZpbGxlX0RhdGEkZW5lcmd5X2RlbWFuZFsyNTpucm93KFNldmlsbGVfRGF0YSldICMgR2V0IGFsbCB0aGUgZGF0YSBleGNlcHQgaW4gZmlyc3Qgcm93ClNldmlsbGVfRGF0YTwtU2V2aWxsZV9EYXRhWzE6KG5yb3coU2V2aWxsZV9EYXRhKS0yNCksXQoKU2V2aWxsZV9EYXRhJEVfMjUgPC0gRV8yNQoKU2V2aWxsZV9EYXRhIDwtIFNldmlsbGVfRGF0YSBbLCBjKCJ0aW1lX0lEIiwgIkVfMSIsIkVfMiIsIkVfMjUiLCAgICJ0ZW1wIiwiaHVtaWRpdHkiLCJwcmVzc3VyZSIsIndpbmRfc3BlZWQiLCJyYWluX2R1cmF0aW9uIiwic25vd19kdXJhdGlvbiIsImRheV9uaWdodCIsInRpbWVfYmFuZCIsInNlYXNvbiIsIndlYXRoZXJfbWFpbiIsImVuZXJneV9kZW1hbmQiKV0KCgojIyMgR2VuZXJhdGUgV29ya2luZyBhbmQgRXZhbHVhdGlvbiBEYXRhCnNldC5zZWVkKDUwMSkKd29ya2luZ19yb3dzIDwtIHNhbXBsZSgxOm5yb3coU2V2aWxsZV9EYXRhKSwwLjgwKm5yb3coU2V2aWxsZV9EYXRhKSkKCndvcmtpbmdfU2V2aWxsZSA8LVNldmlsbGVfRGF0YVt3b3JraW5nX3Jvd3MsXQoKd29ya2luZ19TZXZpbGxlJGluZGV4IDwtIGFzLm51bWVyaWMocm93Lm5hbWVzKHdvcmtpbmdfU2V2aWxsZSkpCndvcmtpbmdfU2V2aWxsZTwtIHdvcmtpbmdfU2V2aWxsZVtvcmRlcih3b3JraW5nX1NldmlsbGUkaW5kZXgpLCBdCgp3b3JraW5nX1NldmlsbGU8LXdvcmtpbmdfU2V2aWxsZSAlPiUKICByZWxvY2F0ZShpbmRleCkgCgoKZXZhbHVhdGlvbl9TZXZpbGxlIDwtU2V2aWxsZV9EYXRhWy13b3JraW5nX3Jvd3MsXQpldmFsdWF0aW9uX1NldmlsbGUkaW5kZXggPC0gYXMubnVtZXJpYyhyb3cubmFtZXMoZXZhbHVhdGlvbl9TZXZpbGxlKSkKZXZhbHVhdGlvbl9TZXZpbGxlPC0gZXZhbHVhdGlvbl9TZXZpbGxlW29yZGVyKGV2YWx1YXRpb25fU2V2aWxsZSRpbmRleCksIF0KCmV2YWx1YXRpb25fU2V2aWxsZTwtZXZhbHVhdGlvbl9TZXZpbGxlICU+JQogIHJlbG9jYXRlKGluZGV4KSAKCndyaXRlLmNzdih3b3JraW5nX1NldmlsbGUsIkRhdGEvd29ya2luZ19TZXZpbGxlLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YoZXZhbHVhdGlvbl9TZXZpbGxlLCJEYXRhL2V2YWx1YXRpb25fU2V2aWxsZS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKCiMjIE1hZHJpZCBEQVRBIFByZXBhcmF0aW9uIApgYGB7cn0KCk1hZHJpZF9yYXc8LXJlYWQuY3N2KGZpbGUgPSAiRGF0YS9NYWRyaWQuY3N2IikKY29sbmFtZXMoTWFkcmlkX3JhdylbY29sbmFtZXMoTWFkcmlkX3JhdykgPT0gIlgiXSA8LSAiRGF0YUlEIgpjb2xuYW1lcyhNYWRyaWRfcmF3KVtjb2xuYW1lcyhNYWRyaWRfcmF3KSA9PSAiZHRfaXNvIl0gPC0gInRpbWUiCk1hZHJpZF9EYXRhPC1NYWRyaWRfcmF3ICU+JQogIGRwbHlyOjpzZWxlY3QoRGF0YUlELHRpbWUsdGVtcCxodW1pZGl0eSxwcmVzc3VyZSx3aW5kX3NwZWVkLHJhaW5fMWgscmFpbl8zaCxzbm93XzNoLHdlYXRoZXJfbWFpbixlbmVyZ3kpCgpNYWRyaWRfRGF0YTwtdHJhbnNmb3JtKE1hZHJpZF9EYXRhLCB3ZWF0aGVyX21haW4gPSBmYWN0b3Iod2VhdGhlcl9tYWluLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJjbGVhciIsICJjbG91ZHMiLCAiZHJpenpsZSIsImR1c3QiLCJmb2ciLCJoYXplIiwibWlzdCIsInJhaW4iLCJzbW9rZSIsInNub3ciLCJzcXVhbGwiLCJ0aHVuZGVyc3Rvcm0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKDE6MTIpKSkKCiMgQWRkIHR3byBjb2x1bW5zIG9mIHJhaW4gZHVyYXRpb24KTWFkcmlkX0RhdGFbInJhaW5fZHVyYXRpb24iXSA8LSBNYWRyaWRfRGF0YSRyYWluXzFoICsgTWFkcmlkX0RhdGEkcmFpbl8zaCAKCiMgUmVuYW1lIENvbHVtbiBOYW1lcyBmb3IgY2xhcml0eSAKTWFkcmlkX0RhdGEgPC1tZXJnZShNYWRyaWRfRGF0YSwgZW5lcmd5X3Jhd1ssYygidGltZSIsIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBCYW5kIChQZWFrLCBPZmYtUGVhaywgTWlkLVBlYWspIildLCBieS54ID0gInRpbWUiLCBieS55ID0gInRpbWUiKQpjb2xuYW1lcyhNYWRyaWRfRGF0YSApW2NvbG5hbWVzKE1hZHJpZF9EYXRhICkgPT0gIlNwZWNpZmllZCBDYXRlZ29yaWNhbCBWYXJpYWJsZTpcclxuVGltZSBCYW5kIChQZWFrLCBPZmYtUGVhaywgTWlkLVBlYWspIl0gPC0gInRpbWVfYmFuZCIKCk1hZHJpZF9EYXRhIDwtbWVyZ2UoTWFkcmlkX0RhdGEsIGVuZXJneV9yYXdbLGMoInRpbWUiLCJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblNlYXNvbiAoU3ByaW5nLCBTdW1tZXIsIEF1dHVtbiwgV2ludGVyKSIpXSwgYnkueCA9ICJ0aW1lIiwgYnkueSA9ICJ0aW1lIikKY29sbmFtZXMoTWFkcmlkX0RhdGEgKVtjb2xuYW1lcyhNYWRyaWRfRGF0YSApID09ICJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblNlYXNvbiAoU3ByaW5nLCBTdW1tZXIsIEF1dHVtbiwgV2ludGVyKSJdIDwtICJzZWFzb24iCgpNYWRyaWRfRGF0YSA8LW1lcmdlKE1hZHJpZF9EYXRhLCBlbmVyZ3lfcmF3WyxjKCJ0aW1lIiwiU3BlY2lmaWVkIENhdGVnb3JpY2FsIFZhcmlhYmxlOlxyXG5UaW1lIG9mIGRheSAoRGF5IHZzIE5pZ2h0KSIpXSwgYnkueCA9ICJ0aW1lIiwgYnkueSA9ICJ0aW1lIikKY29sbmFtZXMoTWFkcmlkX0RhdGEgKVtjb2xuYW1lcyhNYWRyaWRfRGF0YSApID09ICJTcGVjaWZpZWQgQ2F0ZWdvcmljYWwgVmFyaWFibGU6XHJcblRpbWUgb2YgZGF5IChEYXkgdnMgTmlnaHQpIl0gPC0gImRheV9uaWdodCIKCgpjb2xuYW1lcyhNYWRyaWRfRGF0YSApW2NvbG5hbWVzKE1hZHJpZF9EYXRhICkgPT0gInNub3dfM2giXSA8LSAic25vd19kdXJhdGlvbiIKCk1hZHJpZF9EYXRhPC1zdWJzZXQoTWFkcmlkX0RhdGEsc2VsZWN0PS1jKHJhaW5fMWgscmFpbl8zaCxEYXRhSUQpKQoKY29sbmFtZXMoTWFkcmlkX0RhdGEgKVtjb2xuYW1lcyhNYWRyaWRfRGF0YSApID09ICJ0aW1lIl0gPC0gInRpbWVfSUQiCgpNYWRyaWRfRGF0YSA8LSBNYWRyaWRfRGF0YSBbLCBjKCJ0aW1lX0lEIiwidGVtcCIsImh1bWlkaXR5IiwicHJlc3N1cmUiLCJ3aW5kX3NwZWVkIiwicmFpbl9kdXJhdGlvbiIsInNub3dfZHVyYXRpb24iLCJkYXlfbmlnaHQiLCJ0aW1lX2JhbmQiLCJzZWFzb24iLCJ3ZWF0aGVyX21haW4iLCJlbmVyZ3kiKV0KCmNvbG5hbWVzKE1hZHJpZF9EYXRhIClbY29sbmFtZXMoTWFkcmlkX0RhdGEgKSA9PSAiZW5lcmd5Il0gPC0gImVuZXJneV9kZW1hbmQiCgpNRF8gPC0gTWFkcmlkX0RhdGFbTWFkcmlkX0RhdGEkdGVtcCA8IDI3MCAsXQpNYWRyaWRfRGF0YTwtIHN1YnNldChNYWRyaWRfRGF0YSwgdGVtcD4yNzApICMgcmVtb3ZlIHRlbXBlcmF0dXJlIGxlc3MgdGhhbiAyNzBLCk1hZHJpZF9EYXRhPC0gc3Vic2V0KE1hZHJpZF9EYXRhLCBwcmVzc3VyZSA+OTAwICYgcHJlc3N1cmUgPDEwNTApICMgcmVtb3ZlIHByZXNzdXJlIG91dHNpZGUgWzkwMCwxMDUwXW1iYXIKTWFkcmlkX0RhdGE8LXN1YnNldChNYWRyaWRfRGF0YSwgZW5lcmd5X2RlbWFuZCA+MTApICMgUmVtb3ZlIGFsbCAwIGRlbWFuZCBmcm9tIHRoZSBkYXRhCgpNYWRyaWQucmFpbl9kdXJhdGlvbiA8LSBnZ3Bsb3QoTWFkcmlkX0RhdGEpICsKICBnZW9tX3BvaW50KCBhZXMoYygxOmxlbmd0aChyYWluX2R1cmF0aW9uKSkscmFpbl9kdXJhdGlvbikpICsKICBsYWJzKHN1YnRpdGxlPSJSYWluIER1cmF0aW9uIGZvciBNYWRyaWQiLCAKICAgICAgIHk9InJhaW4gZHVyYXRpb24iLCAKICAgICAgIHg9IiMgZGF0YSBwb2ludCIpIAoKTWFkcmlkLmh1bWlkaXR5IDwtIGdncGxvdChNYWRyaWRfRGF0YSkgKwogIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKGh1bWlkaXR5KSksaHVtaWRpdHkpKSArCiAgbGFicyhzdWJ0aXRsZT0iSHVtaWRpdHkgZm9yIE1hZHJpZCIsIAogICAgICAgeT0iaHVtaWRpdHkiLCAKICAgICAgIHg9IiMgZGF0YSBwb2ludCIpICAKCk1hZHJpZC53aW5kX3NwZWVkIDwtIGdncGxvdChNYWRyaWRfRGF0YSkgKwogIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKHdpbmRfc3BlZWQpKSx3aW5kX3NwZWVkKSkgKwogIGxhYnMoc3VidGl0bGU9IldpbmQgU3BlZWQgZm9yIE1hZHJpZCIsIAogICAgICAgeT0id2luZF9zcGVlZCIsIAogICAgICAgeD0iIyBkYXRhIHBvaW50IikgIAoKTWFkcmlkLnByZXNzdXJlIDwtIGdncGxvdChNYWRyaWRfRGF0YSkgKwogIGdlb21fcG9pbnQoIGFlcyhjKDE6bGVuZ3RoKHByZXNzdXJlKSkscHJlc3N1cmUpKSArCiAgbGFicyhzdWJ0aXRsZT0iUHJlc3N1cmUgb2YgTWFkcmlkIiwgCiAgICAgICB5PSJwcmVzc3VyZSIsIAogICAgICAgeD0iIyBkYXRhIHBvaW50IikgIAoKZ2dhcnJhbmdlKE1hZHJpZC5yYWluX2R1cmF0aW9uLE1hZHJpZC5odW1pZGl0eSxNYWRyaWQud2luZF9zcGVlZCxNYWRyaWQucHJlc3N1cmUsCiAgICAgICAgICBsYWJlbHMgPSBjKCIoQSkiLCAiKEIpIiwgIihDKSIsIihEKSIpLAogICAgICAgICAgbmNvbCA9IDIsIG5yb3cgPSAyKQoKCkVfMTwtTWFkcmlkX0RhdGEkZW5lcmd5X2RlbWFuZFstMV0gIyBHZXQgYWxsIHRoZSBkYXRhIGV4Y2VwdCBpbiBmaXJzdCByb3cgCk1hZHJpZF9EYXRhPC1NYWRyaWRfRGF0YVstbnJvdyhNYWRyaWRfRGF0YSksXQpNYWRyaWRfRGF0YSRFXzEgPC0gRV8xCgpFXzI8LUVfMVstMV0KTWFkcmlkX0RhdGE8LU1hZHJpZF9EYXRhWy1ucm93KE1hZHJpZF9EYXRhKSxdCk1hZHJpZF9EYXRhJEVfMiA8LSBFXzIKCkVfMjU8LU1hZHJpZF9EYXRhJGVuZXJneV9kZW1hbmRbMjU6bnJvdyhNYWRyaWRfRGF0YSldICMgR2V0IGFsbCB0aGUgZGF0YSBleGNlcHQgaW4gZmlyc3Qgcm93Ck1hZHJpZF9EYXRhPC1NYWRyaWRfRGF0YVsxOihucm93KE1hZHJpZF9EYXRhKS0yNCksXQoKTWFkcmlkX0RhdGEkRV8yNSA8LSBFXzI1CgpNYWRyaWRfRGF0YSA8LSBNYWRyaWRfRGF0YSBbLCBjKCJ0aW1lX0lEIiwgIkVfMSIsIkVfMiIsIkVfMjUiLCAgICJ0ZW1wIiwiaHVtaWRpdHkiLCJwcmVzc3VyZSIsIndpbmRfc3BlZWQiLCJyYWluX2R1cmF0aW9uIiwic25vd19kdXJhdGlvbiIsImRheV9uaWdodCIsInRpbWVfYmFuZCIsInNlYXNvbiIsIndlYXRoZXJfbWFpbiIsImVuZXJneV9kZW1hbmQiKV0KCgojIyMgR2VuZXJhdGUgV29ya2luZyBhbmQgRXZhbHVhdGlvbiBEYXRhCnNldC5zZWVkKDUwMSkKd29ya2luZ19yb3dzIDwtIHNhbXBsZSgxOm5yb3coTWFkcmlkX0RhdGEpLDAuODAqbnJvdyhNYWRyaWRfRGF0YSkpCgp3b3JraW5nX01hZHJpZCA8LU1hZHJpZF9EYXRhW3dvcmtpbmdfcm93cyxdCgp3b3JraW5nX01hZHJpZCRpbmRleCA8LSBhcy5udW1lcmljKHJvdy5uYW1lcyh3b3JraW5nX01hZHJpZCkpCndvcmtpbmdfTWFkcmlkPC0gd29ya2luZ19NYWRyaWRbb3JkZXIod29ya2luZ19NYWRyaWQkaW5kZXgpLCBdCgp3b3JraW5nX01hZHJpZDwtd29ya2luZ19NYWRyaWQgJT4lCiAgcmVsb2NhdGUoaW5kZXgpIAoKCmV2YWx1YXRpb25fTWFkcmlkIDwtTWFkcmlkX0RhdGFbLXdvcmtpbmdfcm93cyxdCmV2YWx1YXRpb25fTWFkcmlkJGluZGV4IDwtIGFzLm51bWVyaWMocm93Lm5hbWVzKGV2YWx1YXRpb25fTWFkcmlkKSkKZXZhbHVhdGlvbl9NYWRyaWQ8LSBldmFsdWF0aW9uX01hZHJpZFtvcmRlcihldmFsdWF0aW9uX01hZHJpZCRpbmRleCksIF0KCmV2YWx1YXRpb25fTWFkcmlkPC1ldmFsdWF0aW9uX01hZHJpZCAlPiUKICByZWxvY2F0ZShpbmRleCkgCgp3cml0ZS5jc3Yod29ya2luZ19NYWRyaWQsIkRhdGEvd29ya2luZ19NYWRyaWQuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihldmFsdWF0aW9uX01hZHJpZCwiRGF0YS9ldmFsdWF0aW9uX01hZHJpZC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCmBgYAoKIyBNTFIgTW9kZWxsaW5nCgoKCiMjIEJhcmNlbG9uYSBNb2RlbAoKIyMjIERhdGEgUHJvY2Vzc2luZwoKIyMjIyBSZWFkIHdvcmtpbmcgYW5kIGV2YWx1YXRpb24gZGF0YSBzZXQsIG1vcmVvdmVyLCByZW1vdmUgdGhlIGluZGV4IGFuZCB0aW1lIHN0YW1wIGFuZCBzbm93X2R1cmF0aW9uIHZhcmlhYmxlIGFzIHRoZXNlIGFyZSBhbGwgaW52YWxpZCB2YXJpYWJsZXMgKHRoZSBsYXR0ZXIgYXMgYWxsIGVudHJpZXMgYXJlIHplcm8pCgpgYGB7cn0KZXZhbGRhdGE8LXJlYWQuY3N2KCdkYXRhL2V2YWx1YXRpb25fQmFyY2Vsb25hLmNzdicpCndvcmtkYXRhPC1yZWFkLmNzdignZGF0YS93b3JraW5nX0JhcmNlbG9uYS5jc3YnKQojcmVtb3ZlIGluZGV4LCB0aW1lX0lEIGFuZCBzbm93X2R1cmF0aW9uIGNvbHVtbnMgZnJvbSAiZXZhbCIgYW5hbHlzaXMgZGF0YSBmcmFtZQpldmFsQmFyY2Vsb25hPC1ldmFsZGF0YVtjKC0xLC0yLC0xMSldIAojcmVtb3ZlIGluZGV4LCB0aW1lX0lEIGFuZCBzbm93X2R1cmF0aW9uIGNvbHVtbnMgZnJvbSAid29yayIgYW5hbHlzaXMgZGF0YSBmcmFtZQp3b3JrQmFyY2Vsb25hPC13b3JrZGF0YVtjKC0xLC0yLC0xMSldIAojIGhlYWQod29ya0JhcmNlbG9uYSwgNEwpCmBgYAoKCiMjIyBDcmVhdGUgdGhlIGdncGFpciBwbG90IGFuZCBDb3JyZWxhdGlvbiBwbG90CgpgYGB7ciwsZmlnLmhlaWdodD01fQojIENvcnJlbGF0aW9uIG1hdHJpeCBmb3IgbnVtZXJpYyBwcmVkaWN0b3JzIG9ubHkgKGNhdGVnb3JpY2FsIHByZWRpY3RvcnMgZXhjbHVkZWQpCndvcmtjb3I8LXdvcmtCYXJjZWxvbmFbYygtOTotMTIpIF0KZ2dwYWlycyh3b3JrY29yKQpjb3Iod29ya0JhcmNlbG9uYSkKYGBgCgojIyMgQ3JlYXRlIGNvbG9yIHNwZWN0cnVtIGNvcnJlbGF0aW9uIHBsb3QKCmBgYHtyLGZpZy5oZWlnaHQ9NX0KcGRmKCJjb3JwbG90LnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCmNvcnJwbG90KGNvcih3b3JrY29yKSwgbWV0aG9kPSJjb2xvciIsIHR5cGU9ImZ1bGwiLCBhZGRDb2VmLmNvbCA9ICJyZWQiLCB0bC5jb2w9ImJsYWNrIixudW1iZXIuY2V4ID0gMC43NSkKZGV2Lm9mZigpCmNvcnJwbG90KGNvcih3b3JrY29yKSwgbWV0aG9kPSJjb2xvciIsIHR5cGU9ImZ1bGwiLCBhZGRDb2VmLmNvbCA9ICJyZWQiLCB0bC5jb2w9ImJsYWNrIixudW1iZXIuY2V4ID0gMC43NSkKYGBgCgojIyBNb2RlbCBQcm9jZXNzaW5nCgojIyMgQnVpbGQgdGhlIGZ1bGwgbW9kZWwKCmBgYHtyfQp3b3JrTW9kZWwxIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzIgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya0JhcmNlbG9uYSkKYGBgCgojIyMgQ2hlY2sgdGhlIG11bHRpY29sbGluZWFyaXR5IGFzc3VtcHRpb24KCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CnZpZih3b3JrTW9kZWwxKQptdWx0aWNvbF88LXZpZih3b3JrTW9kZWwxKQpwcmludCgiVklGIHZhbHVlcyBncmVhdGVyIHRoYW4gMTAsIGltcGx5aW5nIHNlcmlvdXMgbXVsdGljb2xsaW5lYXJpdHksIGFyZToiKQptdWx0aWNvbF9bbXVsdGljb2xfWywiR1ZJRiJdPjEwLDFdCmBgYAoKQWNjb3JkaW5nIHRvIHRoZSBWSUYgb3V0cHV0IEVfMSBhbmQgRV8yIHZhcmlhYmxlcyBoYXZlIHNlcmlvdXMgbXVsdGljb2xsaW5lYXJpdHkgaXNzdWUgIGFzIHRodXMgcmVtb3ZlIEVfMiBhbmQgcmVidWlsZCB0aGUgbW9kZWwuCgojIyMgRHJvcCBFXzIgdG8gYWRkcmVzcyBtdWx0aWNvbGxpbmVhcml0eSBiZXR3ZWVuIEVfMSBhbmQgRV8yLCBhbmQgcmVidWlsZCB0aGUgbW9kZWwgCgpgYGB7cn0Kd29ya01vZGVsRnVsbCA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtCYXJjZWxvbmEpCnZpZih3b3JrTW9kZWxGdWxsKQpgYGAKCkFjY29yZGluZyB0byB0aGUgVklGIG91dHB1dCwgd2UgY2FuIG9ic2VydmUgdGhhdCB0aGVyZSdzIG5vIHNlcmlvdXMgbXVsdGljb2xsaW5lYXJpdHkgaXNzdWUuIAoKIyMjIE1vZGVsIGFzc3VtcHRpb24gY2hlY2sKCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CiMgdG8gb2J0YWluIHJlc2lkdWFscwpyZXMuZnVsbG1vZGVsMSA8LSByZXNpZHVhbHMod29ya01vZGVsRnVsbCkgCiMgdG8gb2J0YWluIHN0YW5kYXJkaXplZCByZXNpZHVhbHMKc3RkLnJlcy5mdWxsbW9kZWwxIDwtIHJzdGFuZGFyZCh3b3JrTW9kZWxGdWxsKSAKIyB0byBvYnRhaW4gZml0dGVkL3ByZWRpY3RlZCB2YWx1ZXMKcHJlZC5mdWxsbW9kZWwxIDwtIGZpdHRlZC52YWx1ZXMod29ya01vZGVsRnVsbCkgCnBhcihtZnJvdz1jKDEsMSkpCnFxbm9ybSh5ID0gc3RkLnJlcy5mdWxsbW9kZWwxLCBtYWluID0gIiBOb3JtYWwgUS1RIFBsb3QgIiwKICAgICAgIHhsYWIgPSAiVGhlb3JldGljYWwgUXVhbnRpbGVzIiwgeWxhYiA9ICJTYW1wbGUgUXVhbnRpbGVzIikKcXFsaW5lKHkgPSBzdGQucmVzLmZ1bGxtb2RlbDEpCnJlc3Bsb3RkYXRhMSA8LSBkYXRhLmZyYW1lKHN0ZC5yZXMuZnVsbG1vZGVsMSwgcHJlZC5mdWxsbW9kZWwxKQpyZXNiZjEgPC0gbG0oc3RkLnJlcy5mdWxsbW9kZWwxIH4gcHJlZC5mdWxsbW9kZWwxLCBkYXRhID0gcmVzcGxvdGRhdGExKQpwbG90KHggPSBwcmVkLmZ1bGxtb2RlbDEsIHkgPSBzdGQucmVzLmZ1bGxtb2RlbDEsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIHhsYWIgPSAiUHJlZGljdGVkIFZhbHVlcyIsIG1haW4gPSAiUmVzaWR1YWxzIFBsb3QiLCBjb2wgPSBpZmVsc2Uoc3RkLnJlcy5mdWxsbW9kZWwxIDwgLTMsInJlZCIsaWZlbHNlKHN0ZC5yZXMuZnVsbG1vZGVsMSA+IDMsInJlZCIsImJsYWNrIikpKQphYmxpbmUoaCA9IDAsIGNvbD0iYmx1ZSIsIGx0eT0xKQphYmxpbmUocmVzYmYxLCBjb2w9InJlZCIsIGx0eT0zKQphYmxpbmUoaCA9IDMsIGNvbD0iZ3JlZW4iLCBsdHk9MykKYWJsaW5lKGg9LTMsIGNvbD0iZ3JlZW4iLCBsdHk9MykKbGVnZW5kKCJib3R0b21sZWZ0IiwgbGVnZW5kPWMoIkJlc3QgZml0IGxpbmUgb2Ygc3RhbmRhcmRpemVkIHJlc2lkdWFscyIsICJIb3Jpem9udGFsIGxpbmUgeSA9IDAuMCIsICJIb3Jpem9udGFsIGxpbmUsIHkgPSArLy0gMyIpLCBmaWxsID0gYygicmVkIiwiYmx1ZSIsImdyZWVuIiksIGNleCA9IDEuMCkKYGBgCgpBY2NvcmRpbmcgdG8gdGhlIE5vcm1hbCBRUSBwbG90LCB3ZSBjYW4gb2JzZXJ2ZSB0aGF0IG1vc3Qgb2YgcG9pbnRzIGFsaWduIG9uIHRoZSByZWZlcmVuY2UgbGluZSB0aHVzIGl0IGZvbGxvd3MgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gYXNzdW1wdGlvbi4KCkFjY29yZGluZyB0byB0aGUgcmVzaWR1YWwgcGxvdCwgd2UgY2FuIG9ic2VydmUgdGhhdCB0aGVyZSdzIG5vIGhldGVyb3NjZWRhc3RpY2l0eSBpc3N1ZSAsIG5ldmVydGhlbGVzcywgdGhlcmUgYXJlIG51bWVyb3VzIG91dGxpZXJzLgoKIyMjIENoZWNrIHRoZSByZWdyZXNzaW9uIG91dHB1dCBhbmQgYnVpbGQgdGhlIHJlZHVjZSBtb2RlbCAKCiMjIyMgQmFzZWQgb24gcmVncmVzc2lvbiBzdW1tYXJ5LCBwcmVkaWN0b3IgKnJhaW5fZHVyYXRpb24qIGRyb3BwZWQgYXMgaW5zaWduaWZpY2FudCAocC12YWx1ZXMgaXMgPiAwLjA1KS4gIEJ1aWxkIGEgcmVkdWNlIG1vZGVsIHRoZW4gY29tcGFyZSB3aXRoIHRoZSBmdWxsIG1vZGVsCgpgYGB7cn0Kc3VtbWFyeSh3b3JrTW9kZWxGdWxsKQpkZjwtIHdvcmtNb2RlbEZ1bGwgJT4lIAogIHRpZHkoKQojR2V0IHRoZSB2YXJpYWJsZXMgd2hpY2ggdGhlcmUgcC12YWx1ZSBsYXJnZXIgdGhhbiAwLjA1IApkZiU+JQogIGZpbHRlcihkZiRwLnZhbHVlPjAuMDUpCndvcmtNb2RlbFB2YWx1ZSA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya0JhcmNlbG9uYSkKc3VtbWFyeSh3b3JrTW9kZWxQdmFsdWUpCmBgYAoKIyMjIFJlZHVjZWQgbW9kZWwgY29tcGFyaXNvbnMKCisgcC12YWx1ZSBhcHByb2FjaAoKYGBge3J9CmFub3ZhKHdvcmtNb2RlbFB2YWx1ZSwgd29ya01vZGVsRnVsbCkKYGBgCgpBdCA1JSBzaWduaWZpY2FuY2UgbGV2ZWwgLCBubyBzdGF0aXN0aWNhbCBldmlkZW5jZSB0byBzdXBwb3J0IHRoYXQgdGhlIGZ1bGwgbW9kZWwgaXMgcHJlZmVycmVkIG92ZXIgdGhhbiByZWR1Y2VkIG1vZGVsLihpdCdzIHNpbXBsZXIuLi4gbm8gcHJlZGljdGlvbiBwZXJmb3JtYW5jZSBpcyBsb3N0IHZpYSB0aGUgZWxpbWluYXRpb24gb2YgaWRlbnRpZmllZCBwcmVkaWN0b3JzKS4gVGhhdCBpcyB0byBzYXksIHdvcmtNb2RlbFB2YWx1ZSBpcyBwcmVmZXJyZWQgb3ZlciB3b3JrTW9kZWxGdWxsLgoKIAorIEJlc3Qgc3Vic2V0IHNlbGVjdGlvbiBtZXRob2QKCiMjIyMgQmFzZWQgb24gdGhlIHRoZSBCZXN0IHN1YnNldCBzZWxlY3Rpb24gbWV0aG9kLCB3ZSBkcm9wIHByZXNzdWUsIHdpbmRfc3BlZWQsIHJhaW5fZHVyYXRpb24sIHRpbWVfYmFuZCBhbmQgd2VhdGhlcl9tYWluIHRvIGJ1aWxkIHRoZSByZWR1Y2UgbW9kZWwuCgpgYGB7cixmaWcuaGVpZ2h0PTR9CmJlc3RmaXRzIDwtIHJlZ3N1YnNldHMoZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyByYWluX2R1cmF0aW9uICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrQmFyY2Vsb25hLCBuYmVzdCA9IDEpCnBsb3QoYmVzdGZpdHMsIHNjYWxlPSJhZGpyMiIpCndvcmtNb2RlbEJlc3RmaXQ8LWxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3Ioc2Vhc29uKSAsIGRhdGEgPSB3b3JrQmFyY2Vsb25hKQpzdW1tYXJ5KHdvcmtNb2RlbEJlc3RmaXQpCmFub3ZhKHdvcmtNb2RlbEJlc3RmaXQsIHdvcmtNb2RlbFB2YWx1ZSkKYGBgCgpBdCA1JSBzaWduaWZpY2FuY2UgbGV2ZWwgLCBubyBzdGF0aXN0aWNhbCBldmlkZW5jZSB0byBzdXBwb3J0IHRoYXQgdGhlIEJlc3RmaXQgbW9kZWwgaXMgcHJlZmVycmVkIG92ZXIgdGhhbiBQdmFsdWUgbW9kZWwuKGl0J3Mgc2ltcGxlci4uLiBubyBwcmVkaWN0aW9uIHBlcmZvcm1hbmNlIGlzIGxvc3QgdmlhIHRoZSBlbGltaW5hdGlvbiBvZiBpZGVudGlmaWVkIHByZWRpY3RvcnMpLiBUaGF0IGlzIHRvIHNheSwgd29ya01vZGVsUHZhbHVlIGlzIHByZWZlcnJlZCBvdmVyIHdvcmtNb2RlbEJlc3RmaXQuCgorIEFJQyB3aXRoIGZvcndhcmQgc2VsZWN0aW9uIGFwcHJvYWNoIHRlc3QgbWV0aG9kCgpgYGB7cn0Kd29ya051bGxNb2RlbCA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gMSwgZGF0YSA9IHdvcmtCYXJjZWxvbmEpCnN0ZXAud29ya2luZyA8LSBzdGVwQUlDKHdvcmtOdWxsTW9kZWwsIHNjb3BlID0gbGlzdChsb3dlciA9IHdvcmtOdWxsTW9kZWwsCnVwcGVyID0gd29ya01vZGVsRnVsbCksIGRpcmVjdGlvbiA9ICJmb3J3YXJkIiwgdHJhY2U9RkFMU0UpCnN1bW1hcnkoc3RlcC53b3JraW5nKQpgYGAKCiMjIyBBY2NvcmRpbmcgdG8gdGhlIEFJQyBtZXRob2Qgb3V0cHV0LCB3ZSBnZXQgdGhlIHNhbWUgbXVsdGlwbGUgbGluZWFyIG1vZGVsIGFzIGZ1bGwgbW9kZWwuCgorIENyb3NzIFZhbGlkYXRpb24gdGVzdCBtZXRob2QKCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkgaW4gQ1YKc2V0LnNlZWQoMTIzKQojIFNldCB1cCByZXBlYXRlZCBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgd2l0aCBrPTEwCnRyYWluLmNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKQojIFRyYWluIHRoZSBtb2RlbApzdGVwLmN2LndvcmsgPC0gdHJhaW4oZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyByYWluX2R1cmF0aW9uICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrQmFyY2Vsb25hLCBtZXRob2QgPSAibGVhcEJhY2t3YXJkIiwKdHVuZUdyaWQgPSBkYXRhLmZyYW1lKG52bWF4ID0gMToxMSksCnRyQ29udHJvbCA9IHRyYWluLmNvbnRyb2wpCnN0ZXAuY3Yud29yayRyZXN1bHRzCnN1bW1hcnkoc3RlcC5jdi53b3JrJGZpbmFsTW9kZWwpCndvcmtNb2RlbENyb3NzIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtCYXJjZWxvbmEpCmFub3ZhKCB3b3JrTW9kZWxDcm9zcywgd29ya01vZGVsUHZhbHVlKQpzdW1tYXJ5KHdvcmtNb2RlbENyb3NzKQpgYGAKCkF0IDUlIHNpZ25pZmljYW5jZSBsZXZlbCAsIG5vIHN0YXRpc3RpY2FsIGV2aWRlbmNlIHRvIHN1cHBvcnQgdGhhdCB0aGUgQ3Jvc3MgVmFsaWRhdGlvbiBtb2RlbCBpcyBwcmVmZXJyZWQgb3ZlciB0aGFuIFB2YWx1ZSBtb2RlbC4gKExhdHRlciBpcyBzaW1wbGVyLi4uIG5vIHByZWRpY3Rpb24gcGVyZm9ybWFuY2UgaXMgbG9zdCB2aWEgdGhlIGVsaW1pbmF0aW9uIG9mIGlkZW50aWZpZWQgcHJlZGljdG9ycykuIFRoYXQgaXMgdG8gc2F5LCB3b3JrTW9kZWxQdmFsdWUgaXMgcHJlZmVycmVkIG92ZXIgd29ya01vZGVsQ3Jvc3MKCisgT0xTIG1ldGhvZAoKYGBge3IgZmlnLmhlaWdodD00fQojTW9kZWxPTFMgPC0gb2xzX3N0ZXBfYWxsX3Bvc3NpYmxlKHdvcmtNb2RlbEZ1bGwpCiNjYXQoIlxuIikKI3Bsb3QoeCA9IE1vZGVsT0xTJG4sIHkgPSBNb2RlbE9MUyRhZGpyKQojcGxvdCh4ID0gTW9kZWxPTFMkbiwgeSA9IE1vZGVsT0xTJGFpYykKI09MU3N1bW1hcnkgPC0gTW9kZWxPTFMgJT4lIGdyb3VwX2J5KG4pICU+JSAgZmlsdGVyKGFkanIgPT0gbWF4KGFkanIpKQojT0xTc3VtbWFyeQpgYGAKCkJhc2VkIG9uIEFkanVzdGVkIFItc3F1YXJlLCB0aGUgbW9zdCBmYXZvcmFibGUgT0xTIG1vZGVsIGlzIGVxdWl2YWxlbnQgdG8gd29ya01vZGVsRnVsbC4KCiMjIFNlbGVjdCB0aGUgZmluYWwgbW9kZWwKCmBgYHtyfQpNZXRob2QgPC0gYygnRnVsbCcsICdQLXZhbHVlJywgJ0Jlc3RGaXRzJywgJ0FJQyBGb3J3YXJkJywgJ0Nyb3NzIFZhbGlkYXRpb24nLCdPTFNfc3RlcCcpClZhcmlhYmxlcyA8LSBjKDEyLDExLDcsMTIsMTAsMTIpCkNvZWZmaWNpZW50cyA8LSBjKDIyLDIxLDksMjIsMjAsMjEpCkFkanVzdGVkX1IyIDwtIGMoMC45MTU1NTU0LDAuOTE1NTUyMywwLjkxNTE2MzYsMC45MTU1NTU0LDAuOTE1NTExMywwLjkxNTU1MjMpCkJhcmNlbG9uYVRhYmxlPC1kYXRhLmZyYW1lKE1ldGhvZCxWYXJpYWJsZXMsQ29lZmZpY2llbnRzLEFkanVzdGVkX1IyKQojIFN1bW1hcnkgdGFibGUgZm9yIGRpZmZlcmVudCBzZWxlY3Rpb24gbWV0aG9kcwpwcmludChCYXJjZWxvbmFUYWJsZSkKI0FsdGVybmF0ZSBvcmRlcgojQVJTbGlzdCA8LSBsaXN0KEZ1bGxBUlMgPSBjKHN1bW1hcnkod29ya01vZGVsRnVsbCkkYWRqLnIuc3F1YXJlZCksCiMgICAgICAgICAgICAgICAgUHZhbHVlQVJTID0gYyhzdW1tYXJ5KHdvcmtNb2RlbFB2YWx1ZSkkYWRqLnIuc3F1YXJlZCksCiMgICAgICAgICAgICAgICAgQmVzdGZpdHNBUlMgPSBjKHN1bW1hcnkod29ya01vZGVsQmVzdGZpdCkkYWRqLnIuc3F1YXJlZCksCiMgICAgICAgICAgICAgICAgQUlDQVJTID0gYyhzdW1tYXJ5KHN0ZXAud29ya2luZykkYWRqLnIuc3F1YXJlZCksCiMgICAgICAgICAgICAgICAgQ3Jvc3N2QVJTID0gYyhzdW1tYXJ5KHdvcmtNb2RlbENyb3NzKSRhZGouci5zcXVhcmVkKSwKIyAgICAgICAgICAgICAgICBPTFNBUlMgPSBtYXgoT0xTc3VtbWFyeSRhZGpyKSkKI01vZGVsT3JkZXIgPC0gYXMuZGF0YS5mcmFtZShBUlNsaXN0KSAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSAxOjQpICU+JSBhcnJhbmdlKGRlc2ModmFsdWUpKQojTW9kZWxPcmRlcgpgYGAKCk1vZGVsIEFkanVzdGVkIFItc3F1YXJlIGZyb20gYWJvdmUgbWV0aG9kcyAodG8gNCBzaWduaWZpY2FudCBmaWd1cmVzKQoKQSkgRnVsbDogMC45MTU2CgpCKSBQLXZhbHVlOiAwLjkxNTYgKHZpZXdlZCB0byBiZSB0aGUgc2FtZSBhcyBGdWxsKQoKQykgQmVzdGZpdHM6IDAuOTE1MgoKRCkgQUlDIE1ldGhvZDogMC45MTU2CgpFKSBDcm9zcy12YWxpZGF0aW9uOiAwLjkxNTUKCkYpIE9MUy1zdGVwOiAwLjkxNTYgKHZpZXdlZCB0byBiZSB0aGUgc2FtZSBhcyBQLXZhbHVlKQoKKipGaW5hbCBtb2RlbCBpcyB0aGUgd29ya01vZGVsUHZhbHVlIGFzIGl0IGhhcyB0aGUgaGlnaGVzdCBBZGp1c3RlZCBSLXNxdWFyZSBhbmQgZmV3ZXN0IHByZWRpY3RvcnMuKioKCiMjIEJ1aWxkIHRoZSBGaW5hbCBNb2RlbAoKYGBge3J9CndvcmtGaW5hbCA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya0JhcmNlbG9uYSkKYGBgCgoKIyMjIENoZWNrIHRoZSBsaW5lYXJpdHksIGhldGVyb3NjZWRhc3RpY2l0eSwgb3V0bGllciBhbmQgbm9ybWFsIGFzc3VtcHRpb24KCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdz1jKDIsMikpCiNSZXNpZHVhbCBQbG90CnAxPC1wbG90KHg9Zml0dGVkKHdvcmtGaW5hbCksIHk9c3R1ZHJlcyh3b3JrRmluYWwpLCB4bGFiID0gIkZpdHRlZCBWYWx1ZXMiLAp5bGFiID0gIlN0dWRlbnRpemVkIFJlc2lkdWFscyIsIG1haW49J1Jlc2lkdWFscyBQbG90JywgY29sID0gaWZlbHNlKHN0dWRyZXMod29ya0ZpbmFsKSA8IC0zLCJyZWQiLGlmZWxzZShzdHVkcmVzKHdvcmtGaW5hbCkgPiAzLCJyZWQiLCJibGFjayIpKSkKYWJsaW5lKGg9LTMsIGNvbD0icmVkIiwgbHR5PTIpCmFibGluZShoPTMsIGNvbD0icmVkIiwgbHR5PTIpCiNub3JtIHRlc3QKc3RkLnJlcyA8LSByc3RhbmRhcmQod29ya0ZpbmFsKQphZC50ZXN0KHN0ZC5yZXMpCnAyPC1xcW5vcm0oc3R1ZHJlcyh3b3JrRmluYWwpLCBwY2ggPSAxLCBmcmFtZSA9IEZBTFNFLCBtYWluPWV4cHJlc3Npb24oIkFEIHRlc3QgPTIuMioxMCJeLTE2KSkKcXFsaW5lKHN0dWRyZXMod29ya0ZpbmFsKSwgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDIpCiNsZXZlcmFnZSAKbGV2PC1oYXR2YWx1ZXMod29ya0ZpbmFsKQpjdXRsZXYgPSAoMipsZW5ndGgoY29lZmZpY2llbnRzKHdvcmtGaW5hbCkpKS9ucm93KHdvcmtCYXJjZWxvbmEpCiMgQ291bnQgYW5kIGFzc2VzcyB0aGUgbnVtYmVyIG9mIGxldmVyYWdlIHZhbHVlcyA+IGN1dC1vZmYKcG90b3V0bGllciA8LSBzdW0obGV2ID4gY3V0bGV2LCBuYS5ybT1UUlVFKQp0b3Rjb3VudCA9IGxlbmd0aChmaXR0ZWQod29ya0ZpbmFsKSkKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2YgbGV2ZXJhZ2UgcG9pbnRzIHRoYXQgYXJlIHBvdGVudGlhbCBvdXRsaWVycyBpcyIsIHBvdG91dGxpZXIsIi4gIFRoaXMgaXMiLCByb3VuZCgxMDAqcG90b3V0bGllci90b3Rjb3VudCwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHByZWRpY3RlZCB2YWx1ZXMsICgiLCB0b3Rjb3VudCwiKSB0aHVzIG5vdCBtYXRlcmlhbC4iKSkKI2JhcnBsb3QobGV2LCB5bGltID0gYygwLCAyKmN1dGxldikpCnAzPC1wbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIEJhcmNlbG9uYSB3b3JraW5nIHNldCIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCiNjb29rIGRpc3RhbmNlCmNvb2tkaXN0PC1jb29rcy5kaXN0YW5jZSh3b3JrRmluYWwpCiNiYXJwbG90KGNvb2tkaXN0LCB5bGltPWMoMCwxLjAxKSwgbWFpbiA9ICJDb29rJ3MgRGlzdGFuY2UgcGxvdCIpCiNiYXJwbG90KGNvb2tkaXN0LCB5bGltPWMoMCwwLjAwNSksIG1haW4gPSAiQ29vaydzIERpc3RhbmNlIHBsb3QiKQojcGxvdChsb2coY29va2Rpc3QpLCB0eXBlPSJoIiwgeWxpbT1jKDAsMS4wMSksIG1haW4gPSAiQ29vaydzIERpc3RhbmNlIHBsb3QiKQojYWJsaW5lKGggPSAxLCBjb2wgPSAicmVkIikKCmdncGxvdChhcy5kYXRhLmZyYW1lKGNvb2tkaXN0KSxhZXMoeD0xOm5yb3coYXMuZGF0YS5mcmFtZShjb29rZGlzdCkpLHk9Y29va2Rpc3QpKSArCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMSxjb2xvciA9ICJyZWQiKSkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxLjEpKSsKICBzY2FsZV95X2JyZWFrKGJyZWFrcyA9IGMoMC4wMDUsMC45KSwgc2NhbGVzID0wLjEpICsKICBndWlkZXMoY29sb3IgPSAibm9uZSIpICsKICBsYWJzKHRpdGxlPSJDb29rJ3MgRGlzdGFuY2UiLAogICAgICAgIHggPSJJbmRleCIsIHkgPSAiIikKCmBgYAoKUHJlcGFyZSBzdW1tYXJ5IHBsb3QgZm9yIHJlcG9ydApgYGB7cn0KIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoIkJhcmNlbG9uYUFzc3VtcHRpb25DaGVjazEucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKICAgCiMgcGFyKG1mcm93PWMoMiwyKSkKI3Jlc2lkdWFscyBzY2F0dGVyIHBsb3QKcGxvdCh4PWZpdHRlZCh3b3JrRmluYWwpLCB5PXN0dWRyZXMod29ya0ZpbmFsKSwgeGxhYiA9ICJGaXR0ZWQgVmFsdWVzIiwKeWxhYiA9ICJTdHVkZW50aXplZCBSZXNpZHVhbHMiLCBtYWluPSdSZXNpZHVhbHMgUGxvdCcsIGNvbCA9IGlmZWxzZShzdHVkcmVzKHdvcmtGaW5hbCkgPCAtMywicmVkIixpZmVsc2Uoc3R1ZHJlcyh3b3JrRmluYWwpID4gMywicmVkIiwiYmxhY2siKSkpCmFibGluZShoPS0zLCBjb2w9InJlZCIsIGx0eT0yKQphYmxpbmUoaD0zLCBjb2w9InJlZCIsIGx0eT0yKQpncmlkKDEwLDEwKQpkZXYub2ZmKCkKCgojbm9ybWFsaXR5IHBsb3QKcGRmKCJCYXJjZWxvbmFBc3N1bXB0aW9uQ2hlY2syLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCnFxbm9ybShzdHVkcmVzKHdvcmtGaW5hbCksIHBjaCA9IDEsIGZyYW1lID0gRkFMU0UsIG1haW49ZXhwcmVzc2lvbigiQUQgdGVzdCA9Mi4yKjEwIl4tMTYpKQpxcWxpbmUoc3R1ZHJlcyh3b3JrRmluYWwpLCBjb2wgPSAic3RlZWxibHVlIiwgbHdkID0gMikKZ3JpZCgxMCwxMCkKZGV2Lm9mZigpCgoKI2xldmVyYWdlIHBsb3QKcGRmKCJCYXJjZWxvbmFBc3N1bXB0aW9uQ2hlY2szLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCgpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIEJhcmNlbG9uYSB3b3JraW5nIHNldCIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKCiNDb29rJ3MgZGlzdGFuY2UgcGxvdApwZGYoIkJhcmNlbG9uYUFzc3VtcHRpb25DaGVjazQucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKZ2dwbG90KGFzLmRhdGEuZnJhbWUoY29va2Rpc3QpLGFlcyh4PTE6bnJvdyhhcy5kYXRhLmZyYW1lKGNvb2tkaXN0KSkseT1jb29rZGlzdCkpICsKICBnZW9tX2xpbmUoKSArIAogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAxLGNvbG9yID0gInJlZCIpKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEuMSkpKwogIHNjYWxlX3lfYnJlYWsoYnJlYWtzID0gYygwLjAwNSwwLjkpLCBzY2FsZXMgPTAuMSkgKwogIGd1aWRlcyhjb2xvciA9ICJub25lIikgKwogIGxhYnModGl0bGU9IkNvb2sncyBEaXN0YW5jZSIsCiAgICAgICAgeCA9IkluZGV4IiwgeSA9ICIiKQpkZXYub2ZmKCkKCmBgYAoKCgojIyBVc2UgdGhlIGVzdGltYXRlZCBtb2RlbCB0byBwcmVkaWN0IHRoZSB2YWx1ZXMgaW4gdGhlIGV2YWx1YXRpb24gc2V0CmBgYHtyLCBmaWcuaGVpZ2h0PTR9Cm5ld2RhdGEgPC0gZXZhbEJhcmNlbG9uYVsgYygtMiwtMTMpXQpwcmVkaWN0LmV2YWwgPC0gcHJlZGljdCh3b3JrRmluYWwsIG5ld2RhdGEpCnBsb3QoeCA9IGV2YWxCYXJjZWxvbmEkZW5lcmd5X2RlbWFuZCwgeT1wcmVkaWN0LmV2YWwsIHhsYWI9IkFjdHVhbCBlbmVyZ3kgZGVtYW5kIGZvciB0aGUgZXZhbHVhdGlvbiBzZXQiLAp5bGFiPSJQcmVkaWN0ZWQgZW5lcmd5IGRlbWFuZCBmb3IgdGhlIGV2YWx1YXRpb24gc2V0IiwgbWFpbiA9ICJCYXJjZWxvbmEiKQphYmxpbmUoYT0wLCBiPTEsIGNvbD0iYmx1ZSIpCmdyaWQoMTAsMTApCmBgYAoKUHJlcGFyZSByZXBvcnQgZmlndXJlCmBgYHtyIGZpZy5oZWlnaHQ9NH0KIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoIkJhcmNlbG9uYUV2YWxBY3RDb21wLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCiAgIApwYXIobWZyb3c9YygxLDEpKQpwbG90KHggPSBldmFsQmFyY2Vsb25hJGVuZXJneV9kZW1hbmQsIHk9cHJlZGljdC5ldmFsLCB4bGFiPSJBY3R1YWwgZW5lcmd5IGRlbWFuZCBmb3IgdGhlIGV2YWx1YXRpb24gc2V0IiwKeWxhYj0iUHJlZGljdGVkIGVuZXJneSBkZW1hbmQgZm9yIHRoZSBldmFsdWF0aW9uIHNldCIsIG1haW4gPSAiQmFyY2Vsb25hIiwgY2V4LmxhYiA9IDAuNzUpCmFibGluZShhPTAsIGI9MSwgY29sPSJibHVlIikKZ3JpZCgxMCwxMCkKCiMgQ2xvc2luZyB0aGUgZ3JhcGhpY2FsIGRldmljZQpkZXYub2ZmKCkKYGBgCgojIyMgQ29tcHV0ZSB0aGUgUk1TRSBmb3IgdGhlIHByZWRpY3Rpb25zCgpgYGB7cn0Kcm9vdG1zZSA8LSBybXNlKGV2YWxkYXRhJGVuZXJneV9kZW1hbmQsIHByZWRpY3QuZXZhbCkKcHJpbnQocGFzdGUoIlRoZSByb290IG1lYW4gc3F1YXJlIGVycm9yIG9mIHRoZSBwcmVkaWN0ZWQgdnMgYWN0dWFsIixucm93KGV2YWxkYXRhKSwiaG91cmx5IGVuZXJneV9kYXRhIHBvaW50cyBmb3IgQmFyY2Vsb25hIG92ZXIgdGhlIDIwMTUtMjAxOCB0aW1lc3BhbiBpcyBhbiBlbmVyZ3kgZGVtYW5kIGRpZmZlcmVuY2Ugb2YiLHJvdW5kKHJvb3Rtc2UsMiksIk1XaC4iKSkKY2F0KCJcbiIpCmRlbWFuZGNvbXBkZiA8LSBkYXRhLmZyYW1lKCJBY3R1YWwgKE1XaCkiID0gZXZhbEJhcmNlbG9uYSRlbmVyZ3lfZGVtYW5kLCJQcmVkaWN0ZWQgKE1XaCkiID0gcHJlZGljdC5ldmFsKQojIGhlYWQoZGVtYW5kY29tcGRmLDRMKQpjYXQoIlxuIikKcHJpbnQocGFzdGUoIkZvciBleGFtcGxlLCB0aGUgZmlyc3QgYWN0dWFsIGRlbWFuZCB2YWx1ZSBpcyIscm91bmQoZXZhbGRhdGEkZW5lcmd5X2RlbWFuZFsxXSwyKSwiTVdoIGFuZCB0aGUgY29ycmVzcG9uZGluZyBwcmVkaWN0ZWQgdmFsdWUgaXMiLHJvdW5kKHByZWRpY3QuZXZhbFsxXSwyKSwiTVdoLiBUaGUgcGVyY2VudCBlcnJvciBiZXR3ZWVuIHRoaXMgcHJlZGljdGVkIHZhbHVlIHJlbGF0aXZlIHRvIHRoZSBhY3R1YWwgdmFsdWUgaXMiLHJvdW5kKCgxMDAqKHByZWRpY3QuZXZhbFsxXSAtIGV2YWxkYXRhJGVuZXJneV9kZW1hbmRbMV0pL2V2YWxkYXRhJGVuZXJneV9kZW1hbmRbMV0pLDIpLCIlLiIpKQpjYXQoIlxuIikKZGlmZnBjdCA8LSBjKChhYnMoZXZhbGRhdGEkZW5lcmd5X2RlbWFuZCAtIHByZWRpY3QuZXZhbCkpL2V2YWxkYXRhJGVuZXJneV9kZW1hbmQpCnByaW50KHBhc3RlKCJUaGUgbWVhbiBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIixucm93KGV2YWxkYXRhKSwiZXZhbHVhdGlvbiBkYXRhIHNldCBhY3R1YWwgYW5kIG1vZGVsIHByZWRpY3RlZCB2YWx1ZXMgaXMiLHJvdW5kKDEwMCptZWFuKGRpZmZwY3QpLDIpLCIlLiIpKQoKYGBgCgoKIyMjIENvbXB1dGUgYW5kIGFuYWx5emUgSGF0IE1hdHJpeCBmb3IgRXZhbHVhdGlvbiBkYXRhIHNldApPYnRhaW4gWCBtYXRyaXggZnJvbSB0aGUgbW9kZWwKYGBge3J9ClByZWRpY3RvcnMgPC1yb3duYW1lcyhzdW1tYXJ5KHdvcmtGaW5hbCkkY29lZmZpY2llbnRzWyxdKQp3b3JrcHJlZGljdG9ycyA8LSBhcy5kYXRhLmZyYW1lKFByZWRpY3RvcnMpClggPC0gbW9kZWwubWF0cml4KHdvcmtGaW5hbCkKY2xhc3MoWCkKY2F0KCJcbiIpCmRpbShYKQpjYXQoIlxuIikKI2hlYWQoWCwgNEwpCmBgYAoKIyMjIENyZWF0ZSB4X25ldyBtYXRyaXggdXNpbmcgdGhlIHZhbGlkYXRpb24gZGF0YS4gIE5vdGUgdGhhdCB0aGUgcHVycG9zZSBvZiBydW5uaW5nIHRoZSBldmFsdWF0aW9uIHJlZ3Jlc3Npb24gaGVyZSBpcyB0byBleHRyYWN0IGFsbCBvZiB0aGUgY29ycmVjdCBwcmVkaWN0b3IgY29lZmZpY2llbnRzLgpgYGB7cn0KZXZhbEZpbmFsIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5IAogICAgICAgICAgICAgICAgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyBmYWN0b3IoZGF5X25pZ2h0KSAKICAgICAgICAgICAgICAgICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSAKICAgICAgICAgICAgICAgICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSBldmFsQmFyY2Vsb25hKQpQcmVkaWN0b3JzIDwtcm93bmFtZXMoc3VtbWFyeShldmFsRmluYWwpJGNvZWZmaWNpZW50c1ssXSkKZXZhbHByZWRpY3RvcnMgPC0gYXMuZGF0YS5mcmFtZShQcmVkaWN0b3JzKQp4X25ldzEgPC0gbW9kZWwubWF0cml4KGV2YWxGaW5hbCkKY2F0KCJcbiIpCmRpbSh4X25ldzEpCmNhdCgiXG4iKQojQ2hlY2sgaWYgdGhlcmUncyBhIHByZWRpY3RvciBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHdvcmsgYW5kIGV2YWwgbWF0cmljZXMKd29ya2V2YWxjb21wYXJlIDwtYW50aV9qb2luKHdvcmtwcmVkaWN0b3JzLGV2YWxwcmVkaWN0b3JzKQpjYXQoIlxuIikKYGBgCiMjIyBXaGVuIHdvcmtldmFsY29tcGFyZSBpcyBub3QgTnVsbCwgcnVuIHRoZSBmb2xsb3dpbmcgbGluZXMgb2YgY29kZSBhZnRlciBpZGVudGlmeWluZyB3aGljaCBjb2x1bW4gaXMgbWlzc2luZyBiZXR3ZWVuIFggYW5kIHhfbmV3MSAoZm9yIEJhcmNlbG9uYSwgaXQgd2FzIGZvdW5kIHRoYXQgImZhY3Rvcih3ZWF0aGVyX21haW4pNCIgYXBwZWFycyBpbiBXb3JrQmFyY2Vsb25hIGJ1dCBub3QgaW4gZXZhbEJhcmNlbG9uYSkuCgpgYGB7cn0KeF9uZXdfIDwtYXMuZGF0YS5mcmFtZShjYmluZCh4X25ldzEscmVwKDAsbnJvdyh4X25ldzEpKSkpCmR1bW15X3huZXc8LSB4X25ld18gJT4lIHJlbG9jYXRlKFYyMSwuYmVmb3JlID0gMTYpICU+JSByZW5hbWUoImZhY3Rvcih3ZWF0aGVyX21haW4pNCIgPSBWMjEpCnhfbmV3IDwtIGFzLm1hdHJpeChkdW1teV94bmV3KSAgCmNhdCgiXG4iKQpjbGFzcyh4X25ldykKY2F0KCJcbiIpCmRpbSh4X25ldykKY2F0KCJcbiIpCiMgaGVhZCh4X25ldywgNEwpCmBgYAoKCiMjIyBDb21wdXRlIHRoZSBoYXQgdmFsdWVzIGZvciB0aGUgdmFsaWRhdGlvbiBkYXRhCmBgYHtyfQpoX25ld19taWQgPC0gc29sdmUodChYKSUqJVgpCmRpbShoX25ld19taWQpCmhfbmV3IDwtIHhfbmV3JSolaF9uZXdfbWlkJSoldCh4X25ldykKZGltKGhfbmV3KQpgYGAKCiMjIyBQbG90IHRoZSBoYXR2YWx1ZXMgZm9yIHRoZSBkYXRhIGluIHRoZSBldmFsdWF0aW9uIHNldApgYGB7ciBmaWcuaGVpZ2h0PTR9CiNjdXRsZXYyID0gKDIqbGVuZ3RoKGNvZWZmaWNpZW50cyhldmFsRmluYWwpKSkvbnJvdyhldmFsQmFyY2Vsb25hKQojIENvdW50IGFuZCBhc3Nlc3MgdGhlIG51bWJlciBvZiBsZXZlcmFnZSB2YWx1ZXMgPiBjdXQtb2ZmCnBvdG91dGxpZXIyIDwtIHN1bShkaWFnKGhfbmV3KSA+IGN1dGxldiwgbmEucm09VFJVRSkKdG90Y291bnQyID0gbnJvdyhldmFsQmFyY2Vsb25hKQpwbG90KGRpYWcoaF9uZXcpLCB0eXBlID0gImgiLCB5bGFiID0gIkxldmVyYWdlIGZvciB0aGUgdmFsaWRhdGlvbiBkYXRhIHNldCIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCBtYWluID0gIkJhcmNlbG9uYSIpCmFibGluZShoPWN1dGxldiwgY29sPSJyZWQiLCBsdHk9MikKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2YgbGV2ZXJhZ2UgcG9pbnRzIHRoYXQgYXJlIHBvdGVudGlhbCBvdXRsaWVycyBpcyIsIHBvdG91dGxpZXIyLCIuICBUaGlzIGlzIiwgcm91bmQoMTAwKnBvdG91dGxpZXIyL3RvdGNvdW50MiwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHByZWRpY3RlZCB2YWx1ZXMsICgiLCB0b3Rjb3VudDIsIikgdGh1cyBub3QgbWF0ZXJpYWwuIikpCmBgYAoKIyMjIFNpZGUtYnktc2lkZSBjb21wYXJpc2lvbgpgYGB7ciBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdz1jKDEsMikpCnBsb3QobGV2LCB0eXBlPSJoIiwgeWxpbSA9IGMoMCwgMipjdXRsZXYpLCB5bGFiPSJMZXZlcmFnZSBmb3IgdGhlIHdvcmtpbmcgZGF0YSBzZXQiLCBtYWluID0gIkJhcmNlbG9uYSIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCnBsb3QoZGlhZyhoX25ldyksIHR5cGUgPSAiaCIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCB5bGFiID0gIkxldmVyYWdlIGZvciB0aGUgZXZhbHVhdGlvbiBkYXRhIHNldCIsIG1haW4gPSAiQmFyY2Vsb25hIikKYWJsaW5lKGg9Y3V0bGV2LCBjb2w9InJlZCIsIGx0eT0yKQpwcmludChwYXN0ZSgiVGhlIGxldmVyYWdlIGN1dG9mZiBleGNlZWRlbmNlcyBpbiB0aGUgZXZhbHVhdGlvbiBzZXQgaXMiLHJvdW5kKDEwMCpwb3RvdXRsaWVyMi90b3Rjb3VudDIsMiksIiUgb2YgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZlZCB2YWx1ZXMgKCIsIHRvdGNvdW50MiwiKSBhbmQgaXMgY29tcGFyYWJsZSB0byB0aGF0IG9mIHRoZSB3b3JraW5nIHNldCwiLCByb3VuZCgxMDAqcG90b3V0bGllci90b3Rjb3VudCwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHByZWRpY3RlZCB2YWx1ZXMgKCIsIHRvdGNvdW50LCIpLiIpKQpgYGAKClByZXBhcmUgcmVwb3J0IGZpZ3VyZQpgYGB7cn0KIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoIkJhcmNlbG9uYUxldkNvbXAucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSAxNCwgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCiAgIApwYXIobWZyb3c9YygxLDIpKQpwbG90KGxldiwgdHlwZT0iaCIsIHlsYWIgPSAiTGV2ZXJhZ2UiLCB5bGltID0gYygwLDIqY3V0bGV2KSwgeWF4dCA9ICduJywgbWFpbiA9ICJCYXJjZWxvbmEgV29ya2luZyBEYXRhIiwgcmVzID02MDApCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCmF4aXMoc2lkZSA9IDIsIGF0PXNlcSgwLDIqcm91bmQoY3V0bGV2LDUpLGJ5PXJvdW5kKGN1dGxldiw1KSkpCgpwbG90KGRpYWcoaF9uZXcpLCB0eXBlID0gImgiLCB5bGFiID0gIiIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCB5YXh0ID0gJ24nLCBtYWluID0gIkJhcmNlbG9uYSBFdmFsdWF0aW9uIERhdGEiKQphYmxpbmUoaD1jdXRsZXYsIGNvbD0icmVkIiwgbHR5PTIpCmF4aXMoc2lkZSA9IDIsIGF0PXNlcSgwLDIqcm91bmQoY3V0bGV2LDUpLGJ5PXJvdW5kKGN1dGxldiw1KSkpCgojIENsb3NpbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKZGV2Lm9mZigpCmBgYAoKCiMjIEJpbGJhbyBNb2RlbAoKYGBge3J9CmV2YWxkYXRhPC1yZWFkLmNzdignZGF0YS9ldmFsdWF0aW9uX0JpbGJhby5jc3YnKQp3b3JrZGF0YTwtcmVhZC5jc3YoJ2RhdGEvd29ya2luZ19CaWxiYW8uY3N2JykKI3JlbW92ZSBpbmRleCBhbmQgdGltZV9JRCBjb2x1bW5zIGZyb20gImV2YWwiIGFuYWx5c2lzIGRhdGEgZnJhbWUKZXZhbEJpbGJhbzwtZXZhbGRhdGFbYygtMSwtMildIAojcmVtb3ZlIGluZGV4IGFuZCB0aW1lX0lEIGNvbHVtbnMgZnJvbSAid29yayIgYW5hbHlzaXMgZGF0YSBmcmFtZQp3b3JrQmlsYmFvPC13b3JrZGF0YVtjKC0xLC0yKV0gCiMgaGVhZCh3b3JrQmlsYmFvLCA0TCkKYGBgCgoKYGBge3IsLGZpZy5oZWlnaHQ9NX0KIyBDb3JyZWxhdGlvbiBtYXRyaXggZm9yIG51bWVyaWMgcHJlZGljdG9ycyBvbmx5IChjYXRlZ29yaWNhbCBwcmVkaWN0b3JzIGV4Y2x1ZGVkKQp3b3JrY29yPC13b3JrQmlsYmFvW2MoLTEwOi0xMyldCndvcmtjb3JfPC1nZ3BhaXJzKHdvcmtjb3IpCmNvcnZhbHVlczwtIGNvcih3b3JrQmlsYmFvKQojIGNvcnBsb3QyPC0gY29ycnBsb3QoY29yKHdvcmtjb3IpLCBtZXRob2Q9ImNvbG9yIiwgdHlwZT0iZnVsbCIsIGFkZENvZWYuY29sID0gInJlZCIsIHRsLmNvbD0iYmxhY2siLG51bWJlci5jZXggPSAwLjc1KQpgYGAKCgojIyBNb2RlbCBQcm9jZXNzaW5nCgpgYGB7cn0Kd29ya01vZGVsQmlsYmFvMSA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBzbm93X2R1cmF0aW9uICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrQmlsYmFvKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD00fQp2aWZfdmFsdWVzIDwtIHZpZih3b3JrTW9kZWxCaWxiYW8xKQpgYGAKCgpgYGB7cn0Kd29ya01vZGVsQmlsYmFvRnVsbCA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBzbm93X2R1cmF0aW9uICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrQmlsYmFvKQp2aWZfdmFsdWVzMjwtIHZpZih3b3JrTW9kZWxCaWxiYW9GdWxsKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD01fQojIHRvIG9idGFpbiByZXNpZHVhbHMKcmVzLmZ1bGxtb2RlbDEgPC0gcmVzaWR1YWxzKHdvcmtNb2RlbEJpbGJhb0Z1bGwpIAojIHRvIG9idGFpbiBzdGFuZGFyZGl6ZWQgcmVzaWR1YWxzCnN0ZC5yZXMuZnVsbG1vZGVsMSA8LSByc3RhbmRhcmQod29ya01vZGVsQmlsYmFvRnVsbCkgCiMgdG8gb2J0YWluIGZpdHRlZC9wcmVkaWN0ZWQgdmFsdWVzCnByZWQuZnVsbG1vZGVsMSA8LSBmaXR0ZWQudmFsdWVzKHdvcmtNb2RlbEJpbGJhb0Z1bGwpIApwYXIobWZyb3c9YygxLDEpKQpxcW5vcm0oeSA9IHN0ZC5yZXMuZnVsbG1vZGVsMSwgbWFpbiA9ICIgTm9ybWFsIFEtUSBQbG90ICIsCiAgICAgICB4bGFiID0gIlRoZW9yZXRpY2FsIFF1YW50aWxlcyIsIHlsYWIgPSAiU2FtcGxlIFF1YW50aWxlcyIpCnFxbGluZSh5ID0gc3RkLnJlcy5mdWxsbW9kZWwxKQojcmVzaWR1YWwgcGxvdApyZXNwbG90ZGF0YTEgPC0gZGF0YS5mcmFtZShzdGQucmVzLmZ1bGxtb2RlbDEsIHByZWQuZnVsbG1vZGVsMSkKcmVzYmYxIDwtIGxtKHN0ZC5yZXMuZnVsbG1vZGVsMSB+IHByZWQuZnVsbG1vZGVsMSwgZGF0YSA9IHJlc3Bsb3RkYXRhMSkKcGxvdCh4ID0gcHJlZC5mdWxsbW9kZWwxLCB5ID0gc3RkLnJlcy5mdWxsbW9kZWwxLCB5bGFiID0gIlN0YW5kYXJkaXplZCBSZXNpZHVhbHMiLCB4bGFiID0gIlByZWRpY3RlZCBWYWx1ZXMiLCBtYWluID0gIlJlc2lkdWFscyBQbG90IiwgY29sID0gaWZlbHNlKHN0ZC5yZXMuZnVsbG1vZGVsMSA8IC0zLCJyZWQiLGlmZWxzZShzdGQucmVzLmZ1bGxtb2RlbDEgPiAzLCJyZWQiLCJibGFjayIpKSkKYWJsaW5lKGggPSAwLCBjb2w9ImJsdWUiLCBsdHk9MSkKYWJsaW5lKHJlc2JmMSwgY29sPSJyZWQiLCBsdHk9MykKYWJsaW5lKGggPSAzLCBjb2w9ImdyZWVuIiwgbHR5PTMpCmFibGluZShoPS0zLCBjb2w9ImdyZWVuIiwgbHR5PTMpCmxlZ2VuZCgiYm90dG9tbGVmdCIsIGxlZ2VuZD1jKCJCZXN0IGZpdCBsaW5lIG9mIHN0YW5kYXJkaXplZCByZXNpZHVhbHMiLCAiSG9yaXpvbnRhbCBsaW5lIHkgPSAwLjAiLCAiSG9yaXpvbnRhbCBsaW5lLCB5ID0gKy8tIDMiKSwgZmlsbCA9IGMoInJlZCIsImJsdWUiLCJncmVlbiIpLCBjZXggPSAxLjApCmBgYAoKYGBge3J9CiMgc3VtbWFyeSh3b3JrTW9kZWxCaWxiYW9GdWxsKQpkZjwtIHdvcmtNb2RlbEJpbGJhb0Z1bGwgJT4lIAogIHRpZHkoKQojR2V0IHRoZSB2YXJpYWJsZXMgd2hpY2ggdGhlcmUgcC12YWx1ZSBsYXJnZXIgdGhhbiAwLjA1IApkZiU+JQogIGZpbHRlcihkZiRwLnZhbHVlPjAuMDUpCndvcmtNb2RlbEJpbGJhb1B2YWx1ZSA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtCaWxiYW8pCiNzdW1tYXJ5KHdvcmtNb2RlbEJpbGJhb1B2YWx1ZSkKYGBgCgojIyMgUmVkdWNlZCBtb2RlbCBjb21wYXJpc29ucwoKKyBwLXZhbHVlIGFwcHJvYWNoCgpgYGB7cn0KYW5vdmFjaGVjazE8LWFub3ZhKHdvcmtNb2RlbEJpbGJhb1B2YWx1ZSwgd29ya01vZGVsQmlsYmFvRnVsbCkKYGBgCgorIEJlc3Qgc3Vic2V0IHNlbGVjdGlvbiBtZXRob2QKCmBgYHtyLCBmaWcuaGVpZ2h0PTV9CmJlc3RmaXRzIDwtIHJlZ3N1YnNldHMoZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyByYWluX2R1cmF0aW9uICsgc25vd19kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya0JpbGJhbywgbmJlc3QgPSAxKQojIHBsb3QoYmVzdGZpdHMsIHNjYWxlPSJhZGpyMiIpCmBgYAoKYGBge3J9CndvcmtNb2RlbEJpbGJhb0Jlc3RmaXQ8LWxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyByYWluX2R1cmF0aW9uICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtCaWxiYW8pCiMgc3VtbWFyeSh3b3JrTW9kZWxCaWxiYW9CZXN0Zml0KQphbm92YWNoZWNrMjwtYW5vdmEod29ya01vZGVsQmlsYmFvQmVzdGZpdCwgd29ya01vZGVsQmlsYmFvUHZhbHVlKQpgYGAKCisgQUlDIHdpdGggZm9yd2FyZCBzZWxlY3Rpb24gYXBwcm9hY2ggdGVzdCBtZXRob2QKCmBgYHtyfQp3b3JrTnVsbE1vZGVsPC0gbG0oZW5lcmd5X2RlbWFuZCB+IDEsIGRhdGEgPSB3b3JrQmlsYmFvKQpzdGVwLndvcmtpbmcgPC0gc3RlcEFJQyh3b3JrTnVsbE1vZGVsLCBzY29wZSA9IGxpc3QobG93ZXIgPSB3b3JrTnVsbE1vZGVsLAp1cHBlciA9IHdvcmtNb2RlbEJpbGJhb0Z1bGwpLCBkaXJlY3Rpb24gPSAiZm9yd2FyZCIsIHRyYWNlPUZBTFNFKQojIHN1bW1hcnkoc3RlcC53b3JraW5nKQpgYGAKClNhbWUgcmVkdWNlZCBtb2RlbCBhcyBQLVZhbHVlIGFwcHJvYWNoCgorIENyb3NzIFZhbGlkYXRpb24gdGVzdCBtZXRob2QKCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkgaW4gQ1YKc2V0LnNlZWQoMTIzKQojIFNldCB1cCByZXBlYXRlZCBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgd2l0aCBrPTEwCnRyYWluLmNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKQojIFRyYWluIHRoZSBtb2RlbApzdGVwLmN2LndvcmsgPC0gdHJhaW4oZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyByYWluX2R1cmF0aW9uICsgc25vd19kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya0JpbGJhbywgbWV0aG9kID0gImxlYXBCYWNrd2FyZCIsCnR1bmVHcmlkID0gZGF0YS5mcmFtZShudm1heCA9IDE6MTIpLAp0ckNvbnRyb2wgPSB0cmFpbi5jb250cm9sKQojIHN0ZXAuY3Yud29yayRyZXN1bHRzCiMgc3VtbWFyeShzdGVwLmN2LndvcmskZmluYWxNb2RlbCkKYGBgCgpgYGB7cn0Kd29ya01vZGVsQmlsYmFvQ3Jvc3MgPC0gbG0oZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtCaWxiYW8pCmFub3ZhY2hlY2szPC1hbm92YSggd29ya01vZGVsQmlsYmFvQ3Jvc3MsIHdvcmtNb2RlbEJpbGJhb1B2YWx1ZSkKIyBzdW1tYXJ5KHdvcmtNb2RlbEJpbGJhb0Nyb3NzKQpgYGAKCisgT0xTIG1ldGhvZAoKYGBge3IgZmlnLmhlaWdodD00fQojTW9kZWxPTFMgPC0gb2xzX3N0ZXBfYWxsX3Bvc3NpYmxlKHdvcmtNb2RlbEJpbGJhb0Z1bGwpCiNjYXQoIlxuIikKI3Bsb3QoeCA9IE1vZGVsT0xTJG4sIHkgPSBNb2RlbE9MUyRhZGpyKQojcGxvdCh4ID0gTW9kZWxPTFMkbiwgeSA9IE1vZGVsT0xTJGFpYykKI09MU3N1bW1hcnkgPC0gTW9kZWxPTFMgJT4lIGdyb3VwX2J5KG4pICU+JSAgZmlsdGVyKGFkanIgPT0gbWF4KGFkanIpKQojY2F0KCJcbiIpCiNPTFNzdW1tYXJ5CmBgYAoKU2FtZSByZWR1Y2VkIG1vZGVsIGFzIFAtVmFsdWUgYXBwcm9hY2ggKHRodXMgY29tbWVudGluZyBvdXQgZ2l2ZW4gcHJvY2Vzc2luZyB0aW1lKQoKIyMgU2VsZWN0IHRoZSBmaW5hbCBtb2RlbAoKTW9kZWwgYWRqdXN0ZWQgUi1zcXVhcmUgZnJvbSBhYm92ZSBtZXRob2RzCgpBKSBGdWxsOiAwLjkxNTQKCkIpIFAtdmFsdWU6IDAuOTE1NAoKQykgQmVzdGZpdHM6IDAuOTE0OQoKRCkgQUlDIE1ldGhvZDogMC45MTU0CgpFKSBDcm9zcy12YWxpZGF0aW9uOiAwLjkxNTQKCkYpIE9MUy1zdGVwOiAwLjkxNTQKCkZpbmFsIG1vZGVsIGlzIHRoZSB3b3JrTW9kZWxCaWxiYW9QdmFsdWUgd2l0aCBoaWdoZXN0IEFkanVzdGVkIFItc3F1YXJlIGFuZCBmZXdlc3QgcHJlZGljdG9ycy4KCmBgYHtyfQpNZXRob2QgPC0gYygnRnVsbCcsICdQLXZhbHVlJywgJ0Jlc3RGaXRzJywgJ0FJQyBGb3J3YXJkJywgJ0Nyb3NzIFZhbGlkYXRpb24nLCdPTFNfc3RlcCcpClZhcmlhYmxlcyA8LSBjKDEzLDEyLDksMTIsMTEsMTIpCkNvZWZmaWNpZW50cyA8LSBjKDIzLDIyLDE2LDIyLDIxLDIyKQpBZGp1c3RlZF9SMiA8LSBjKDAuOTE1Mzg2NSwwLjkxNTM4ODYsMC45MTQ4OTEzLDAuOTE1Mzg4NiwwLjkxNTM2ODAsMC45MTUzODg2KQpCaWxiYW9UYWJsZTwtZGF0YS5mcmFtZShNZXRob2QsVmFyaWFibGVzLENvZWZmaWNpZW50cyxBZGp1c3RlZF9SMikKYGBgCgojIyMgQnVpbGQgdGhlIEZpbmFsIE1vZGVsCgpgYGB7cn0Kd29ya0ZpbmFsIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya0JpbGJhbykKYGBgCgoKIyMjIENoZWNrIHRoZSBsaW5lYXJpdHksIGhldGVyb3NjZWRhc3RpY2l0eSwgb3V0bGllciBhbmQgbm9ybWFsIGFzc3VtcHRpb24KCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdz1jKDIsMikpCiNSZXNpZHVhbCBQbG90CnBsb3QoeD1maXR0ZWQod29ya0ZpbmFsKSwgeT1zdHVkcmVzKHdvcmtGaW5hbCksIHhsYWIgPSAiRml0dGVkIFZhbHVlcyIsCnlsYWIgPSAiU3R1ZGVudGl6ZWQgUmVzaWR1YWxzIiwgbWFpbj0nUmVzaWR1YWxzIFBsb3QnLCBjb2wgPSBpZmVsc2Uoc3R1ZHJlcyh3b3JrRmluYWwpIDwgLTMsInJlZCIsaWZlbHNlKHN0dWRyZXMod29ya0ZpbmFsKSA+IDMsInJlZCIsImJsYWNrIikpKQphYmxpbmUoaD0tMywgY29sPSJyZWQiLCBsdHk9MikKYWJsaW5lKGg9MywgY29sPSJyZWQiLCBsdHk9MikKI25vcm0gdGVzdApzdGQucmVzIDwtIHJzdGFuZGFyZCh3b3JrRmluYWwpCmFkLnRlc3Qoc3RkLnJlcykKcXFub3JtKHN0dWRyZXMod29ya0ZpbmFsKSwgcGNoID0gMSwgZnJhbWUgPSBGQUxTRSwgbWFpbj1leHByZXNzaW9uKCJBRCB0ZXN0ID0yLjIqMTAiXi0xNikpCnFxbGluZShzdHVkcmVzKHdvcmtGaW5hbCksIGNvbCA9ICJzdGVlbGJsdWUiLCBsd2QgPSAyKQojbGV2ZXJhZ2UgCmxldjwtaGF0dmFsdWVzKHdvcmtGaW5hbCkKY3V0bGV2ID0gKDIqbGVuZ3RoKGNvZWZmaWNpZW50cyh3b3JrRmluYWwpKSkvbnJvdyh3b3JrQmlsYmFvKQojIENvdW50IGFuZCBhc3Nlc3MgdGhlIG51bWJlciBvZiBsZXZlcmFnZSB2YWx1ZXMgPiBjdXQtb2ZmCnBvdG91dGxpZXIgPC0gc3VtKGxldiA+IGN1dGxldiwgbmEucm09VFJVRSkKdG90Y291bnQgPSBsZW5ndGgoZml0dGVkKHdvcmtGaW5hbCkpCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIGxldmVyYWdlIHBvaW50cyB0aGF0IGFyZSBwb3RlbnRpYWwgb3V0bGllcnMgaXMiLCBwb3RvdXRsaWVyLCIuICBUaGlzIGlzIiwgcm91bmQoMTAwKnBvdG91dGxpZXIvdG90Y291bnQsMiksIiUgb2YgdGhlIHRvdGFsIG51bWJlciBvZiBwcmVkaWN0ZWQgdmFsdWVzLCAoIiwgdG90Y291bnQsIikgdGh1cyBub3QgbWF0ZXJpYWwuIikpCiNiYXJwbG90KGxldiwgeWxpbSA9IGMoMCwgMipjdXRsZXYpKQpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIEJpbGJhbyB3b3JraW5nIHNldCIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCiNjb29rIGRpc3RhbmNlCmNvb2tkaXN0PC1jb29rcy5kaXN0YW5jZSh3b3JrRmluYWwpCiNiYXJwbG90KGNvb2tkaXN0LCB5bGltPWMoMCwxLjAxKSwgbWFpbiA9ICJDb29rJ3MgRGlzdGFuY2UgcGxvdCIpCiNiYXJwbG90KGNvb2tkaXN0LCB5bGltPWMoMCwwLjAwNSksIG1haW4gPSAiQ29vaydzIERpc3RhbmNlIHBsb3QiKQojcGxvdCgoY29va2Rpc3QpLCB0eXBlPSJoIiwgeWxpbT1jKDAsMC4wNSksIG1haW4gPSAiQ29vaydzIERpc3RhbmNlIHBsb3QiKQojYWJsaW5lKGggPSAxLCBjb2wgPSAicmVkIikKZ2dwbG90KGFzLmRhdGEuZnJhbWUoY29va2Rpc3QpLGFlcyh4PTE6bnJvdyhhcy5kYXRhLmZyYW1lKGNvb2tkaXN0KSkseT1jb29rZGlzdCkpICsKICBnZW9tX2xpbmUoKSArIAogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAxLGNvbG9yID0gInJlZCIpKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEuMSkpKwogIHNjYWxlX3lfYnJlYWsoYnJlYWtzID0gYygwLjAxNSwwLjkpLCBzY2FsZXMgPTAuMSkgKwogIGd1aWRlcyhjb2xvciA9ICJub25lIikgKwogIGxhYnModGl0bGU9IkNvb2sncyBEaXN0YW5jZSIsCiAgICAgICAgeCA9IkluZGV4IiwgeSA9ICIiKQpgYGAKClByZXBhcmUgc3VtbWFyeSBwbG90IGZvciByZXBvcnQKYGBge3J9CiMgT3BlbmluZyB0aGUgZ3JhcGhpY2FsIGRldmljZQojIEN1c3RvbWl6aW5nIHRoZSBvdXRwdXQKcGRmKCJCaWxiYW9Bc3N1bXB0aW9uQ2hlY2sxLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCiAgIAojIHBhcihtZnJvdz1jKDIsMikpCiNyZXNpZHVhbHMgc2NhdHRlciBwbG90CnBsb3QoeD1maXR0ZWQod29ya0ZpbmFsKSwgeT1zdHVkcmVzKHdvcmtGaW5hbCksIHhsYWIgPSAiRml0dGVkIFZhbHVlcyIsCnlsYWIgPSAiU3R1ZGVudGl6ZWQgUmVzaWR1YWxzIiwgbWFpbj0nUmVzaWR1YWxzIFBsb3QnLCBjb2wgPSBpZmVsc2Uoc3R1ZHJlcyh3b3JrRmluYWwpIDwgLTMsInJlZCIsaWZlbHNlKHN0dWRyZXMod29ya0ZpbmFsKSA+IDMsInJlZCIsImJsYWNrIikpKQphYmxpbmUoaD0tMywgY29sPSJyZWQiLCBsdHk9MikKYWJsaW5lKGg9MywgY29sPSJyZWQiLCBsdHk9MikKZ3JpZCgxMCwxMCkKZGV2Lm9mZigpCgoKI25vcm1hbGl0eSBwbG90CnBkZigiQmlsYmFvQXNzdW1wdGlvbkNoZWNrMi5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDcsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbApxcW5vcm0oc3R1ZHJlcyh3b3JrRmluYWwpLCBwY2ggPSAxLCBmcmFtZSA9IEZBTFNFLCBtYWluPWV4cHJlc3Npb24oIkFEIHRlc3QgPTIuMioxMCJeLTE2KSkKcXFsaW5lKHN0dWRyZXMod29ya0ZpbmFsKSwgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDIpCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKCiNsZXZlcmFnZSBwbG90CnBkZigiQmlsYmFvQXNzdW1wdGlvbkNoZWNrMy5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDcsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbAoKcGxvdChsZXYsIHR5cGU9ImgiLCB5bGltID0gYygwLCAyKmN1dGxldiksIHlsYWI9IkxldmVyYWdlIGZvciBCYXJjZWxvbmEgd29ya2luZyBzZXQiKQphYmxpbmUoaCA9IGN1dGxldiwgY29sID0gInJlZCIsIGx0eT0yKQpncmlkKDEwLDEwKQpkZXYub2ZmKCkKCgojQ29vaydzIGRpc3RhbmNlIHBsb3QKcGRmKCJCaWxiYW9Bc3N1bXB0aW9uQ2hlY2s0LnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCmdncGxvdChhcy5kYXRhLmZyYW1lKGNvb2tkaXN0KSxhZXMoeD0xOm5yb3coYXMuZGF0YS5mcmFtZShjb29rZGlzdCkpLHk9Y29va2Rpc3QpKSArCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMSxjb2xvciA9ICJyZWQiKSkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxLjEpKSsKICBzY2FsZV95X2JyZWFrKGJyZWFrcyA9IGMoMC4wMDUsMC45KSwgc2NhbGVzID0wLjEpICsKICBndWlkZXMoY29sb3IgPSAibm9uZSIpICsKICBsYWJzKHRpdGxlPSJDb29rJ3MgRGlzdGFuY2UiLAogICAgICAgIHggPSJJbmRleCIsIHkgPSAiIikKZGV2Lm9mZigpCgpgYGAKCgoKCgoKIyMjIFVzZSB0aGUgZXN0aW1hdGVkIG1vZGVsIHRvIHByZWRpY3QgdGhlIHZhbHVlcyBpbiB0aGUgZXZhbHVhdGlvbiBzZXQKCmBgYHtyIGZpZy5oZWlnaHQ9NH0KbmV3ZGF0YSA8LSBldmFsQmlsYmFvWyBjKC0yLC0xNCldCnByZWRpY3QuZXZhbCA8LSBwcmVkaWN0KHdvcmtGaW5hbCwgbmV3ZGF0YSkKcGxvdCh4ID0gZXZhbEJpbGJhbyRlbmVyZ3lfZGVtYW5kLCB5PXByZWRpY3QuZXZhbCwgeGxhYj0iQWN0dWFsIGVuZXJneSBkZW1hbmQgZm9yIHRoZSBldmFsdWF0aW9uIHNldCIsCnlsYWI9IlByZWRpY3RlZCBlbmVyZ3kgZGVtYW5kIGZvciB0aGUgZXZhbHVhdGlvbiBzZXQiLCBtYWluID0gIkJpbGJhbyIpCmFibGluZShhPTAsIGI9MSwgY29sPSJibHVlIikKZ3JpZCgxMCwxMCkKYGBgCgpQcmVwYXJlIHJlcG9ydCBmaWd1cmUKYGBge3IgZmlnLmhlaWdodD00fQojIE9wZW5pbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKIyBDdXN0b21pemluZyB0aGUgb3V0cHV0CnBkZigiQmlsYmFvRXZhbEFjdENvbXAucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKICAgCnBhcihtZnJvdz1jKDEsMSkpCnBsb3QoeCA9IGV2YWxCaWxiYW8kZW5lcmd5X2RlbWFuZCwgeT1wcmVkaWN0LmV2YWwsIHhsYWI9IkFjdHVhbCBlbmVyZ3kgZGVtYW5kIGZvciB0aGUgZXZhbHVhdGlvbiBzZXQiLAp5bGFiPSJQcmVkaWN0ZWQgZW5lcmd5IGRlbWFuZCBmb3IgdGhlIGV2YWx1YXRpb24gc2V0IiwgbWFpbiA9ICJCaWxiYW8iLCBjZXgubGFiID0gMC43NSkKYWJsaW5lKGE9MCwgYj0xLCBjb2w9ImJsdWUiKQpncmlkKDEwLDEwKQoKIyBDbG9zaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCmRldi5vZmYoKQpgYGAKCiMjIyBDb21wdXRlIHRoZSBSTVNFIGZvciB0aGUgcHJlZGljdGlvbnMKCmBgYHtyfQpyb290bXNlIDwtIHJtc2UoZXZhbEJpbGJhbyRlbmVyZ3lfZGVtYW5kLCBwcmVkaWN0LmV2YWwpCnByaW50KHBhc3RlKCJUaGUgcm9vdCBtZWFuIHNxdWFyZSBlcnJvciBvZiB0aGUgcHJlZGljdGVkIHZzIGFjdHVhbCIsbnJvdyhldmFsZGF0YSksImhvdXJseSBlbmVyZ3lfZGF0YSBwb2ludHMgZm9yIEJpbGJhbyBvdmVyIHRoZSAyMDE1LTIwMTggdGltZXNwYW4gaXMgYW4gZW5lcmd5IGRlbWFuZCBkaWZmZXJlbmNlIG9mIixyb3VuZChyb290bXNlLDIpLCJNV2guIikpCmNhdCgiXG4iKQpkZW1hbmRjb21wZGYgPC0gZGF0YS5mcmFtZSgiQWN0dWFsIChNV2gpIiA9IGV2YWxCaWxiYW8kZW5lcmd5X2RlbWFuZCwiUHJlZGljdGVkIChNV2gpIiA9IHByZWRpY3QuZXZhbCkKIyBoZWFkKGRlbWFuZGNvbXBkZiw0TCkKY2F0KCJcbiIpCnByaW50KHBhc3RlKCJGb3IgZXhhbXBsZSwgdGhlIGZpcnN0IGFjdHVhbCBkZW1hbmQgdmFsdWUgaXMiLHJvdW5kKGV2YWxkYXRhJGVuZXJneV9kZW1hbmRbMV0sMiksIk1XaCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgcHJlZGljdGVkIHZhbHVlIGlzIixyb3VuZChwcmVkaWN0LmV2YWxbMV0sMiksIk1XaC4gVGhlIHBlcmNlbnQgZXJyb3IgYmV0d2VlbiB0aGlzIHByZWRpY3RlZCB2YWx1ZSByZWxhdGl2ZSB0byB0aGUgYWN0dWFsIHZhbHVlIGlzIixyb3VuZCgoMTAwKihwcmVkaWN0LmV2YWxbMV0gLSBldmFsZGF0YSRlbmVyZ3lfZGVtYW5kWzFdKS9ldmFsZGF0YSRlbmVyZ3lfZGVtYW5kWzFdKSwyKSwiJS4iKSkKY2F0KCJcbiIpCmRpZmZwY3QgPC0gYygoYWJzKGV2YWxkYXRhJGVuZXJneV9kZW1hbmQgLSBwcmVkaWN0LmV2YWwpKS9ldmFsZGF0YSRlbmVyZ3lfZGVtYW5kKQpwcmludChwYXN0ZSgiVGhlIG1lYW4gZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSIsbnJvdyhldmFsZGF0YSksImV2YWx1YXRpb24gZGF0YSBzZXQgYWN0dWFsIGFuZCBtb2RlbCBwcmVkaWN0ZWQgdmFsdWVzIGlzIixyb3VuZCgxMDAqbWVhbihkaWZmcGN0KSwyKSwiJS4iKSkKYGBgCgojIyBDb21wdXRlIGFuZCBhbmFseXplIEhhdCBNYXRyaXggZm9yIEV2YWx1YXRpb24gZGF0YSBzZXQKT2J0YWluIFggbWF0cml4IGZyb20gdGhlIG1vZGVsCmBgYHtyfQpQcmVkaWN0b3JzIDwtcm93bmFtZXMoc3VtbWFyeSh3b3JrRmluYWwpJGNvZWZmaWNpZW50c1ssXSkKd29ya3ByZWRpY3RvcnMgPC0gYXMuZGF0YS5mcmFtZShQcmVkaWN0b3JzKQpYIDwtIG1vZGVsLm1hdHJpeCh3b3JrRmluYWwpCmNsYXNzKFgpCmNhdCgiXG4iKQpkaW0oWCkKY2F0KCJcbiIpCiMgaGVhZChYLCA0TCkKYGBgCgpDcmVhdGUgeF9uZXcgbWF0cml4IHVzaW5nIHRoZSB2YWxpZGF0aW9uIGRhdGEuICBOb3RlIHRoYXQgdGhlIHB1cnBvc2Ugb2YgcnVubmluZyB0aGUgZXZhbHVhdGlvbiByZWdyZXNzaW9uIGhlcmUgaXMgdG8gZXh0cmFjdCBhbGwgb2YgdGhlIGNvcnJlY3QgcHJlZGljdG9yIGNvZWZmaWNpZW50cy4KYGBge3J9CmV2YWxGaW5hbCA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IGV2YWxCaWxiYW8pClByZWRpY3RvcnMgPC1yb3duYW1lcyhzdW1tYXJ5KGV2YWxGaW5hbCkkY29lZmZpY2llbnRzWyxdKQpldmFscHJlZGljdG9ycyA8LSBhcy5kYXRhLmZyYW1lKFByZWRpY3RvcnMpCnhfbmV3MSA8LSBtb2RlbC5tYXRyaXgoZXZhbEZpbmFsKQpjYXQoIlxuIikKZGltKHhfbmV3MSkKY2F0KCJcbiIpCiNDaGVjayBpZiB0aGVyZSdzIGEgcHJlZGljdG9yIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgd29yayBhbmQgZXZhbCBtYXRyaWNlcwp3b3JrZXZhbGNvbXBhcmUgPC1hbnRpX2pvaW4od29ya3ByZWRpY3RvcnMsZXZhbHByZWRpY3RvcnMpCmBgYAoKIyMjIElmIHdvcmtldmFsY29tcGFyZSBpcyAnTm8gZGF0YSBhdmFpbGFibGUgaW4gdGFibGUnLCBydW4gdGhlIG5leHQgbGluZSBvZiBjb2RlIGFuZCBza2lwIHRvIGxpbmUgODE2CmBgYHtyfQp4X25ldyA8LSB4X25ldzEKYGBgCgoKIyMjIE90aGVyd2lzZSB3aGVuIHdvcmtldmFsY29tcGFyZSBpcyBub3QgJ05vIGRhdGEgYXZhaWxhYmxlIGluIHRhYmxlJywgcnVuIHRoZSBmb2xsb3dpbmcgbGluZXMgb2YgY29kZSBhZnRlciBpZGVudGlmeWluZyB3aGljaCBjb2x1bW4gaXMgbWlzc2luZyBiZXR3ZWVuIFggYW5kIHhfbmV3MSAobm8gZGlzY3JlcGVuY3kgd2FzIGZvdW5kIGJldHdlZW4gd29ya0JpbGJhbyBidXQgbm90IGluIGV2YWxCaWxiYW8pLgoKYGBge3J9CiN4X25ld18gPC1hcy5kYXRhLmZyYW1lKGNiaW5kKHhfbmV3MSxyZXAoMCxucm93KHhfbmV3MSkpKSkKI2R1bW15X3huZXc8LSB4X25ld18gJT4lIHJlbG9jYXRlKFYyMSwuYmVmb3JlID0gMTYpICU+JSByZW5hbWUoImZhY3Rvcih3ZWF0aGVyX21haW4pNCIgPSBWMjEpCiN4X25ldyA8LSBhcy5tYXRyaXgoZHVtbXlfeG5ldykgIApjYXQoIlxuIikKY2xhc3MoeF9uZXcpCmNhdCgiXG4iKQpkaW0oeF9uZXcpCmNhdCgiXG4iKQojIGhlYWQoeF9uZXcsIDRMKQpgYGAKCgoKCiMjIyBDb21wdXRlIHRoZSBoYXQgdmFsdWVzIGZvciB0aGUgdmFsaWRhdGlvbiBkYXRhCmBgYHtyfQpoX25ld19taWQgPC0gc29sdmUodChYKSUqJVgpCmRpbShoX25ld19taWQpCmhfbmV3IDwtIHhfbmV3JSolaF9uZXdfbWlkJSoldCh4X25ldykKZGltKGhfbmV3KQpgYGAKClBsb3QgdGhlIGhhdHZhbHVlcyBmb3IgdGhlIGRhdGEgaW4gdGhlIGV2YWx1YXRpb24gc2V0CmBgYHtyIGZpZy5oZWlnaHQ9NH0KI2N1dGxldjIgPSAoMipsZW5ndGgoY29lZmZpY2llbnRzKGV2YWxGaW5hbCkpKS9ucm93KGV2YWxCYXJjZWxvbmEpCiMgQ291bnQgYW5kIGFzc2VzcyB0aGUgbnVtYmVyIG9mIGxldmVyYWdlIHZhbHVlcyA+IGN1dC1vZmYKcG90b3V0bGllcjIgPC0gc3VtKGRpYWcoaF9uZXcpID4gY3V0bGV2LCBuYS5ybT1UUlVFKQp0b3Rjb3VudDIgPSBucm93KGV2YWxCaWxiYW8pCiMgcGxvdChkaWFnKGhfbmV3KSwgdHlwZSA9ICJoIiwgeWxhYiA9ICJMZXZlcmFnZSBmb3IgdGhlIHZhbGlkYXRpb24gZGF0YSBzZXQiLCB5bGltID0gYygwLDIqY3V0bGV2KSwgbWFpbiA9ICJCaWxiYW8iKQojIGFibGluZShoPWN1dGxldiwgY29sPSJyZWQiLCBsdHk9MikKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2YgbGV2ZXJhZ2UgcG9pbnRzIHRoYXQgYXJlIHBvdGVudGlhbCBvdXRsaWVycyBpcyIsIHBvdG91dGxpZXIyLCIuICBUaGlzIGlzIiwgcm91bmQoMTAwKnBvdG91dGxpZXIyL3RvdGNvdW50MiwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHByZWRpY3RlZCB2YWx1ZXMsICgiLCB0b3Rjb3VudDIsIikgdGh1cyBub3QgbWF0ZXJpYWwuIikpCmBgYAoKU2lkZS1ieS1zaWRlIGNvbXBhcmlzaW9uCmBgYHtyIGZpZy5oZWlnaHQ9NH0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdChsZXYsIHR5cGU9ImgiLCB5bGltID0gYygwLCAyKmN1dGxldiksIHlsYWI9IkxldmVyYWdlIGZvciB0aGUgd29ya2luZyBkYXRhIHNldCIsIG1haW4gPSAiQmFyY2Vsb25hIikKYWJsaW5lKGggPSBjdXRsZXYsIGNvbCA9ICJyZWQiLCBsdHk9MikKcGxvdChkaWFnKGhfbmV3KSwgdHlwZSA9ICJoIiwgeWxpbSA9IGMoMCwyKmN1dGxldiksIHlsYWIgPSAiTGV2ZXJhZ2UgZm9yIHRoZSBldmFsdWF0aW9uIGRhdGEgc2V0IiwgbWFpbiA9ICJCYXJjZWxvbmEiKQphYmxpbmUoaD1jdXRsZXYsIGNvbD0icmVkIiwgbHR5PTIpCnByaW50KHBhc3RlKCJUaGUgbGV2ZXJhZ2UgY3V0b2ZmIGV4Y2VlZGVuY2VzIGluIHRoZSBldmFsdWF0aW9uIHNldCBpcyIscm91bmQoMTAwKnBvdG91dGxpZXIyL3RvdGNvdW50MiwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIG9ic2VydmVkIHZhbHVlcyAoIiwgdG90Y291bnQyLCIpIGFuZCBpcyBjb21wYXJhYmxlIHRvIHRoYXQgb2YgdGhlIHdvcmtpbmcgc2V0LCIsIHJvdW5kKDEwMCpwb3RvdXRsaWVyL3RvdGNvdW50LDIpLCIlIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdGVkIHZhbHVlcyAoIiwgdG90Y291bnQsIikuIikpCmBgYAoKUHJlcGFyZSByZXBvcnQgZmlndXJlCmBgYHtyfQojIE9wZW5pbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKIyBDdXN0b21pemluZyB0aGUgb3V0cHV0CnBkZigiQmlsYmFvTGV2Q29tcC5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDE0LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKICAgCnBhcihtZnJvdz1jKDEsMikpCnBsb3QobGV2LCB0eXBlPSJoIiwgeWxhYiA9ICJMZXZlcmFnZSIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCB5YXh0ID0gJ24nLCBtYWluID0gIkJpbGJhbyBXb3JraW5nIERhdGEiLCByZXMgPTYwMCkKYWJsaW5lKGggPSBjdXRsZXYsIGNvbCA9ICJyZWQiLCBsdHk9MikKYXhpcyhzaWRlID0gMiwgYXQ9c2VxKDAsMipyb3VuZChjdXRsZXYsNSksYnk9cm91bmQoY3V0bGV2LDUpKSkKCnBsb3QoZGlhZyhoX25ldyksIHR5cGUgPSAiaCIsIHlsYWIgPSAiIiwgeWxpbSA9IGMoMCwyKmN1dGxldiksIHlheHQgPSAnbicsIG1haW4gPSAiQmlsYmFvIEV2YWx1YXRpb24gRGF0YSIpCmFibGluZShoPWN1dGxldiwgY29sPSJyZWQiLCBsdHk9MikKYXhpcyhzaWRlID0gMiwgYXQ9c2VxKDAsMipyb3VuZChjdXRsZXYsNSksYnk9cm91bmQoY3V0bGV2LDUpKSkKCiMgQ2xvc2luZyB0aGUgZ3JhcGhpY2FsIGRldmljZQpkZXYub2ZmKCkKYGBgCgojIyBNYWRyaWQgTW9kZWwKCmBgYHtyfQpldmFsZGF0YTwtcmVhZC5jc3YoJ2RhdGEvZXZhbHVhdGlvbl9NYWRyaWQuY3N2JykKd29ya2RhdGE8LXJlYWQuY3N2KCdkYXRhL3dvcmtpbmdfTWFkcmlkLmNzdicpCiNyZW1vdmUgaW5kZXggYW5kIHRpbWVfSUQgY29sdW1ucyBmcm9tICJldmFsIiBhbmFseXNpcyBkYXRhZnJhbWUKZXZhbE1hZHJpZDwtZXZhbGRhdGFbYygtMSwtMildIAojcmVtb3ZlIGluZGV4IGFuZCB0aW1lX0lEIGNvbHVtbnMgZnJvbSAid29yayIgYW5hbHlzaXMgZGF0YWZyYW1lCndvcmtNYWRyaWQ8LXdvcmtkYXRhW2MoLTEsLTIpXSAKaGVhZCh3b3JrTWFkcmlkLCA0TCkKYGBgCgoKYGBge3IsLGZpZy5oZWlnaHQ9NX0KIyBDb3JyZWxhdGlvbiBtYXRyaXggZm9yIG51bWVyaWMgcHJlZGljdG9ycyBvbmx5IChjYXRlZ29yaWNhbCBwcmVkaWN0b3JzIGV4Y2x1ZGVkKQp3b3JrY29yPC13b3JrTWFkcmlkW2MoLTEwOi0xMykgXQpnZ3BhaXJzKHdvcmtjb3IpCmNvcih3b3JrTWFkcmlkKQpgYGAKCgpgYGB7cixmaWcuaGVpZ2h0PTR9CmNvcnJwbG90KGNvcih3b3JrY29yKSwgbWV0aG9kPSJjb2xvciIsIHR5cGU9ImZ1bGwiLCBhZGRDb2VmLmNvbCA9ICJyZWQiLCB0bC5jb2w9ImJsYWNrIixudW1iZXIuY2V4ID0gMC43NSkKYGBgCgoKYGBge3J9CndvcmtNb2RlbE1hZHJpZDEgPC0gbG0oZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMiArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyByYWluX2R1cmF0aW9uICsgc25vd19kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya01hZHJpZCkKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9NH0KdmlmKHdvcmtNb2RlbE1hZHJpZDEpCmBgYAoKCmBgYHtyfQp3b3JrTW9kZWxNYWRyaWRGdWxsIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIHNub3dfZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtNYWRyaWQpCnZpZih3b3JrTW9kZWxNYWRyaWRGdWxsKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD01fQojIHRvIG9idGFpbiByZXNpZHVhbHMKcmVzLmZ1bGxtb2RlbDEgPC0gcmVzaWR1YWxzKHdvcmtNb2RlbE1hZHJpZEZ1bGwpIAojIHRvIG9idGFpbiBzdGFuZGFyZGl6ZWQgcmVzaWR1YWxzCnN0ZC5yZXMuZnVsbG1vZGVsMSA8LSByc3RhbmRhcmQod29ya01vZGVsTWFkcmlkRnVsbCkgCiMgdG8gb2J0YWluIGZpdHRlZC9wcmVkaWN0ZWQgdmFsdWVzCnByZWQuZnVsbG1vZGVsMSA8LSBmaXR0ZWQudmFsdWVzKHdvcmtNb2RlbE1hZHJpZEZ1bGwpIApwYXIobWZyb3c9YygxLDEpKQpxcW5vcm0oeSA9IHN0ZC5yZXMuZnVsbG1vZGVsMSwgbWFpbiA9ICIgTm9ybWFsIFEtUSBQbG90ICIsCiAgICAgICB4bGFiID0gIlRoZW9yZXRpY2FsIFF1YW50aWxlcyIsIHlsYWIgPSAiU2FtcGxlIFF1YW50aWxlcyIpCnFxbGluZSh5ID0gc3RkLnJlcy5mdWxsbW9kZWwxKQojcmVzaWR1YWwgcGxvdApyZXNwbG90ZGF0YTEgPC0gZGF0YS5mcmFtZShzdGQucmVzLmZ1bGxtb2RlbDEsIHByZWQuZnVsbG1vZGVsMSkKcmVzYmYxIDwtIGxtKHN0ZC5yZXMuZnVsbG1vZGVsMSB+IHByZWQuZnVsbG1vZGVsMSwgZGF0YSA9IHJlc3Bsb3RkYXRhMSkKcGxvdCh4ID0gcHJlZC5mdWxsbW9kZWwxLCB5ID0gc3RkLnJlcy5mdWxsbW9kZWwxLCB5bGFiID0gIlN0YW5kYXJkaXplZCBSZXNpZHVhbHMiLCB4bGFiID0gIlByZWRpY3RlZCBWYWx1ZXMiLCBtYWluID0gIlJlc2lkdWFscyBQbG90IiwgY29sID0gaWZlbHNlKHN0ZC5yZXMuZnVsbG1vZGVsMSA8IC0zLCJyZWQiLGlmZWxzZShzdGQucmVzLmZ1bGxtb2RlbDEgPiAzLCJyZWQiLCJibGFjayIpKSkKYWJsaW5lKGggPSAwLCBjb2w9ImJsdWUiLCBsdHk9MSkKYWJsaW5lKHJlc2JmMSwgY29sPSJyZWQiLCBsdHk9MykKYWJsaW5lKGggPSAzLCBjb2w9ImdyZWVuIiwgbHR5PTMpCmFibGluZShoPS0zLCBjb2w9ImdyZWVuIiwgbHR5PTMpCmxlZ2VuZCgiYm90dG9tbGVmdCIsIGxlZ2VuZD1jKCJCZXN0IGZpdCBsaW5lIG9mIHN0YW5kYXJkaXplZCByZXNpZHVhbHMiLCAiSG9yaXpvbnRhbCBsaW5lIHkgPSAwLjAiLCAiSG9yaXpvbnRhbCBsaW5lLCB5ID0gKy8tIDMiKSwgZmlsbCA9IGMoInJlZCIsImJsdWUiLCJncmVlbiIpLCBjZXggPSAwLjUpCmBgYAoKYGBge3J9CnN1bW1hcnkod29ya01vZGVsTWFkcmlkRnVsbCkKZGY8LSB3b3JrTW9kZWxNYWRyaWRGdWxsICU+JSAKICB0aWR5KCkKI0dldCB0aGUgdmFyaWFibGVzIHdoaWNoIHRoZXJlIHAtdmFsdWUgbGFyZ2VyIHRoYW4gMC4wNSAKZGYlPiUKICBmaWx0ZXIoZGYkcC52YWx1ZT4wLjA1KQp3b3JrTW9kZWxNYWRyaWRQdmFsdWUgPC0gbG0oZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyByYWluX2R1cmF0aW9uICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrTWFkcmlkKQpzdW1tYXJ5KHdvcmtNb2RlbE1hZHJpZFB2YWx1ZSkKYGBgCgorIHAtdmFsdWUgYXBwcm9hY2gKCmBgYHtyfQphbm92YSh3b3JrTW9kZWxNYWRyaWRQdmFsdWUsIHdvcmtNb2RlbE1hZHJpZEZ1bGwpCmBgYAoKKyBCZXN0IHN1YnNldCBzZWxlY3Rpb24gbWV0aG9kCgpgYGB7ciwgZmlnLmhlaWdodD01fQpiZXN0Zml0cyA8LSByZWdzdWJzZXRzKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIHNub3dfZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtNYWRyaWQsIG5iZXN0ID0gMSkKcGxvdChiZXN0Zml0cywgc2NhbGU9ImFkanIyIikKYGBgCgpgYGB7cn0Kd29ya01vZGVsTWFkcmlkQmVzdGZpdDwtbG0oZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgd2luZF9zcGVlZCArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSAsIGRhdGEgPSB3b3JrTWFkcmlkKQpzdW1tYXJ5KHdvcmtNb2RlbE1hZHJpZEJlc3RmaXQpCmFub3ZhKHdvcmtNb2RlbE1hZHJpZEJlc3RmaXQsIHdvcmtNb2RlbE1hZHJpZFB2YWx1ZSkKYGBgCgorIEFJQyB3aXRoIGZvcndhcmQgc2VsZWN0aW9uIGFwcHJvYWNoIHRlc3QgbWV0aG9kCgpgYGB7cn0Kd29ya051bGxNb2RlbDwtIGxtKGVuZXJneV9kZW1hbmQgfiAxLCBkYXRhID0gd29ya01hZHJpZCkKc3RlcC53b3JraW5nIDwtIHN0ZXBBSUMod29ya051bGxNb2RlbCwgc2NvcGUgPSBsaXN0KGxvd2VyID0gd29ya051bGxNb2RlbCwKdXBwZXIgPSB3b3JrTW9kZWxNYWRyaWRGdWxsKSwgZGlyZWN0aW9uID0gImZvcndhcmQiLCB0cmFjZT1GQUxTRSkKc3VtbWFyeShzdGVwLndvcmtpbmcpCmBgYAoKU2FtZSByZWR1Y2VkIG1vZGVsIGFzIFAtdmFsdWUKCisgQ3Jvc3MgVmFsaWRhdGlvbiB0ZXN0IG1ldGhvZAoKYGBge3IsbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBTZXQgc2VlZCBmb3IgcmVwcm9kdWNpYmlsaXR5IGluIENWCnNldC5zZWVkKDEyMykKIyBTZXQgdXAgcmVwZWF0ZWQgay1mb2xkIGNyb3NzLXZhbGlkYXRpb24sIHdpdGggaz0xMAp0cmFpbi5jb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkKIyBUcmFpbiB0aGUgbW9kZWwKc3RlcC5jdi53b3JrIDwtIHRyYWluKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIHNub3dfZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtNYWRyaWQsIG1ldGhvZCA9ICJsZWFwQmFja3dhcmQiLAp0dW5lR3JpZCA9IGRhdGEuZnJhbWUobnZtYXggPSAxOjEyKSwKdHJDb250cm9sID0gdHJhaW4uY29udHJvbCkKc3RlcC5jdi53b3JrJHJlc3VsdHMKc3VtbWFyeShzdGVwLmN2LndvcmskZmluYWxNb2RlbCkKYGBgCgoKYGBge3J9CndvcmtNb2RlbE1hZHJpZENyb3NzIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtNYWRyaWQpCmFub3ZhKCB3b3JrTW9kZWxNYWRyaWRDcm9zcywgd29ya01vZGVsTWFkcmlkUHZhbHVlKQpzdW1tYXJ5KHdvcmtNb2RlbE1hZHJpZENyb3NzKQpgYGAKCisgT0xTIG1ldGhvZAoKYGBge3IgZmlnLmhlaWdodD00fQojTW9kZWxPTFMgPC0gb2xzX3N0ZXBfYWxsX3Bvc3NpYmxlKHdvcmtNb2RlbE1hZHJpZEZ1bGwpCiNjYXQoIlxuIikKI3Bsb3QoeCA9IE1vZGVsT0xTJG4sIHkgPSBNb2RlbE9MUyRhZGpyKQojcGxvdCh4ID0gTW9kZWxPTFMkbiwgeSA9IE1vZGVsT0xTJGFpYykKI09MU3N1bW1hcnkgPC0gTW9kZWxPTFMgJT4lIGdyb3VwX2J5KG4pICU+JSAgZmlsdGVyKGFkanIgPT0gbWF4KGFkanIpKQojT0xTc3VtbWFyeQpgYGAKClNhbWUgcmVkdWNlZCBtb2RlbCBhcyBQLVZhbHVlIGFwcHJvYWNoICh0aHVzIGNvbW1lbnRpbmcgb3V0IGdpdmVuIHByb2Nlc3NpbmcgdGltZSkKCiMjIFNlbGVjdCB0aGUgZmluYWwgbW9kZWwKCk1vZGVsIGFkanVzdGVkIFItc3F1YXJlIGZyb20gYWJvdmUgbWV0aG9kcyAodG8gZm91ciBzaWduaWZpY2FudCBkaWdpdHMpCgpBKSBGdWxsOiAwLjkxNgoKQikgUC12YWx1ZTogMC45MTYKCkMpIEJlc3RmaXRzOiAwLjkxNTQKCkQpIEFJQyBNZXRob2Q6IDAuOTE2CgpFKSBDcm9zcy12YWxpZGF0aW9uOiAwLjkxNgoKRikgT0xTLXN0ZXA6IDAuOTE2CgpGaW5hbCBtb2RlbCBpcyB0aGUgd29ya01vZGVsTWFkcmlkUHZhbHVlIHdpdGggaGlnaGVzdCBBZGp1c3RlZCBSLXNxdWFyZSBhbmQgZmV3ZXN0IHByZWRpY3RvcnMuCgpgYGB7cn0KTWV0aG9kIDwtIGMoJ0Z1bGwnLCAnUC12YWx1ZScsICdCZXN0Rml0cycsICdBSUMgRm9yd2FyZCcsICdDcm9zcyBWYWxpZGF0aW9uJykKVmFyaWFibGVzIDwtIGMoMTMsMTIsOCwxMiwxMSkKQ29lZmZpY2llbnRzIDwtIGMoMjMsMjIsMTEsMjIsMjEpCkFkanVzdGVkX1IyIDwtIGMoMC45MTYwMzI1LDAuOTE2MDMwOSwwLjkxNTM1OTgsMC45MTYwMzA5LDAuOTE1OTk3NCkKTWFkcmlkVGFibGU8LWRhdGEuZnJhbWUoTWV0aG9kLFZhcmlhYmxlcyxDb2VmZmljaWVudHMsQWRqdXN0ZWRfUjIpCmBgYAoKIyMjIEJ1aWxkIHRoZSBGaW5hbCBNb2RlbAoKYGBge3J9CndvcmtGaW5hbCA8LSB3b3JrTW9kZWxNYWRyaWRQdmFsdWUKYGBgCgoKIyMjIENoZWNrIHRoZSBsaW5lYXJpdHksIGhldGVyb3NjZWRhc3RpY2l0eSwgb3V0bGllciBhbmQgbm9ybWFsIGFzc3VtcHRpb24KCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdz1jKDIsMikpCiNSZXNpZHVhbCBQbG90CnBsb3QoeD1maXR0ZWQod29ya0ZpbmFsKSwgeT1zdHVkcmVzKHdvcmtGaW5hbCksIHhsYWIgPSAiRml0dGVkIFZhbHVlcyIsCnlsYWIgPSAiU3R1ZGVudGl6ZWQgUmVzaWR1YWxzIiwgbWFpbj0nUmVzaWR1YWxzIFBsb3QnLCBjb2wgPSBpZmVsc2Uoc3R1ZHJlcyh3b3JrRmluYWwpIDwgLTMsInJlZCIsaWZlbHNlKHN0dWRyZXMod29ya0ZpbmFsKSA+IDMsInJlZCIsImJsYWNrIikpKQphYmxpbmUoaD0tMywgY29sPSJyZWQiLCBsdHk9MikKYWJsaW5lKGg9MywgY29sPSJyZWQiLCBsdHk9MikKI25vcm0gdGVzdApzdGQucmVzIDwtIHJzdGFuZGFyZCh3b3JrRmluYWwpCmFkLnRlc3Qoc3RkLnJlcykKcXFub3JtKHN0dWRyZXMod29ya0ZpbmFsKSwgcGNoID0gMSwgZnJhbWUgPSBGQUxTRSwgbWFpbj1leHByZXNzaW9uKCJBRCB0ZXN0ID0yLjIqMTAiXi0xNikpCnFxbGluZShzdHVkcmVzKHdvcmtGaW5hbCksIGNvbCA9ICJzdGVlbGJsdWUiLCBsd2QgPSAyKQojbGV2ZXJhZ2UgCmxldjwtaGF0dmFsdWVzKHdvcmtGaW5hbCkKY3V0bGV2ID0gKDIqbGVuZ3RoKGNvZWZmaWNpZW50cyh3b3JrRmluYWwpKSkvbnJvdyh3b3JrTWFkcmlkKQojIENvdW50IGFuZCBhc3Nlc3MgdGhlIG51bWJlciBvZiBsZXZlcmFnZSB2YWx1ZXMgPiBjdXQtb2ZmCnBvdG91dGxpZXIgPC0gc3VtKGxldiA+IGN1dGxldiwgbmEucm09VFJVRSkKdG90Y291bnQgPSBsZW5ndGgoZml0dGVkKHdvcmtGaW5hbCkpCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIGxldmVyYWdlIHBvaW50cyB0aGF0IGFyZSBwb3RlbnRpYWwgb3V0bGllcnMgaXMiLCBwb3RvdXRsaWVyLCIuICBUaGlzIGlzIiwgcm91bmQoMTAwKnBvdG91dGxpZXIvdG90Y291bnQsMiksIiUgb2YgdGhlIHRvdGFsIG51bWJlciBvZiBwcmVkaWN0ZWQgdmFsdWVzLCAoIiwgdG90Y291bnQsIikgdGh1cyBub3QgbWF0ZXJpYWwuIikpCiNiYXJwbG90KGxldiwgeWxpbSA9IGMoMCwgMipjdXRsZXYpKQpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIHRoZSB3b3JraW5nIHNldCIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCiNjb29rIGRpc3RhbmNlCmNvb2tkaXN0PC1jb29rcy5kaXN0YW5jZSh3b3JrRmluYWwpCiNiYXJwbG90KGNvb2tkaXN0LCB5bGltPWMoMCwxLjAxKSwgbWFpbiA9ICJDb29rJ3MgRGlzdGFuY2UgcGxvdCIpCiNhYmxpbmUoaCA9IDEsIGNvbCA9ICJyZWQiKQpnZ3Bsb3QoYXMuZGF0YS5mcmFtZShjb29rZGlzdCksYWVzKHg9MTpucm93KGFzLmRhdGEuZnJhbWUoY29va2Rpc3QpKSx5PWNvb2tkaXN0KSkgKwogIGdlb21fbGluZSgpICsgCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDEsY29sb3IgPSAicmVkIikpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMS4xKSkrCiAgc2NhbGVfeV9icmVhayhicmVha3MgPSBjKDAuMDEsMC45KSwgc2NhbGVzID0wLjEpICsKICBndWlkZXMoY29sb3IgPSAibm9uZSIpICsKICBsYWJzKHRpdGxlPSJDb29rJ3MgRGlzdGFuY2UiLAogICAgICAgIHggPSJJbmRleCIsIHkgPSAiIikKYGBgCgpgYGB7cn0KIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoIk1hZHJpZEFzc3VtcHRpb25DaGVjazEucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKICAgCiMgcGFyKG1mcm93PWMoMiwyKSkKI3Jlc2lkdWFscyBzY2F0dGVyIHBsb3QKcGxvdCh4PWZpdHRlZCh3b3JrRmluYWwpLCB5PXN0dWRyZXMod29ya0ZpbmFsKSwgeGxhYiA9ICJGaXR0ZWQgVmFsdWVzIiwKeWxhYiA9ICJTdHVkZW50aXplZCBSZXNpZHVhbHMiLCBtYWluPSdSZXNpZHVhbHMgUGxvdCcsIGNvbCA9IGlmZWxzZShzdHVkcmVzKHdvcmtGaW5hbCkgPCAtMywicmVkIixpZmVsc2Uoc3R1ZHJlcyh3b3JrRmluYWwpID4gMywicmVkIiwiYmxhY2siKSkpCmFibGluZShoPS0zLCBjb2w9InJlZCIsIGx0eT0yKQphYmxpbmUoaD0zLCBjb2w9InJlZCIsIGx0eT0yKQpncmlkKDEwLDEwKQpkZXYub2ZmKCkKCgojbm9ybWFsaXR5IHBsb3QKcGRmKCJNYWRyaWRBc3N1bXB0aW9uQ2hlY2syLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCnFxbm9ybShzdHVkcmVzKHdvcmtGaW5hbCksIHBjaCA9IDEsIGZyYW1lID0gRkFMU0UsIG1haW49ZXhwcmVzc2lvbigiQUQgdGVzdCA9Mi4yKjEwIl4tMTYpKQpxcWxpbmUoc3R1ZHJlcyh3b3JrRmluYWwpLCBjb2wgPSAic3RlZWxibHVlIiwgbHdkID0gMikKZ3JpZCgxMCwxMCkKZGV2Lm9mZigpCgoKI2xldmVyYWdlIHBsb3QKcGRmKCJNYWRyaWRBc3N1bXB0aW9uQ2hlY2szLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCgpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIEJhcmNlbG9uYSB3b3JraW5nIHNldCIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKCiNDb29rJ3MgZGlzdGFuY2UgcGxvdApwZGYoIk1hZHJpZEFzc3VtcHRpb25DaGVjazQucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKZ2dwbG90KGFzLmRhdGEuZnJhbWUoY29va2Rpc3QpLGFlcyh4PTE6bnJvdyhhcy5kYXRhLmZyYW1lKGNvb2tkaXN0KSkseT1jb29rZGlzdCkpICsKICBnZW9tX2xpbmUoKSArIAogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSAxLGNvbG9yID0gInJlZCIpKSsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEuMSkpKwogIHNjYWxlX3lfYnJlYWsoYnJlYWtzID0gYygwLjAwNSwwLjkpLCBzY2FsZXMgPTAuMSkgKwogIGd1aWRlcyhjb2xvciA9ICJub25lIikgKwogIGxhYnModGl0bGU9IkNvb2sncyBEaXN0YW5jZSIsCiAgICAgICAgeCA9IkluZGV4IiwgeSA9ICIiKQpkZXYub2ZmKCkKCmBgYAoKIyMjIFVzZSB0aGUgZXN0aW1hdGVkIG1vZGVsIHRvIHByZWRpY3QgdGhlIHZhbHVlcyBpbiB0aGUgZXZhbHVhdGlvbiBzZXQKCmBgYHtyfQpuZXdkYXRhIDwtIGV2YWxNYWRyaWRbIGMoLTIsLTE0KV0KcHJlZGljdC5ldmFsIDwtIHByZWRpY3Qod29ya0ZpbmFsLCBuZXdkYXRhKQpwbG90KHggPSBldmFsTWFkcmlkJGVuZXJneV9kZW1hbmQsIHk9cHJlZGljdC5ldmFsLCB4bGFiPSJBY3R1YWwgZW5lcmd5IGRlbWFuZCBmb3IgdGhlIGV2YWx1YXRpb24gc2V0IiwKeWxhYj0iUHJlZGljdGVkIGVuZXJneSBkZW1hbmQgZm9yIHRoZSBldmFsdWF0aW9uIHNldCIsIG1haW4gPSAiTWFkcmlkIikKYWJsaW5lKGE9MCwgYj0xLCBjb2w9ImJsdWUiKQpncmlkKDEwLDEwKQpgYGAKClByZXBhcmUgcmVwb3J0IGZpZ3VyZQpgYGB7ciBmaWcuaGVpZ2h0PTR9CiMgT3BlbmluZyB0aGUgZ3JhcGhpY2FsIGRldmljZQojIEN1c3RvbWl6aW5nIHRoZSBvdXRwdXQKcGRmKCJNYWRyaWRFdmFsQWN0Q29tcC5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDcsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbAogICAKcGFyKG1mcm93PWMoMSwxKSkKcGxvdCh4ID0gZXZhbE1hZHJpZCRlbmVyZ3lfZGVtYW5kLCB5PXByZWRpY3QuZXZhbCwgeGxhYj0iQWN0dWFsIGVuZXJneSBkZW1hbmQgZm9yIHRoZSBldmFsdWF0aW9uIHNldCIsCnlsYWI9IlByZWRpY3RlZCBlbmVyZ3kgZGVtYW5kIGZvciB0aGUgZXZhbHVhdGlvbiBzZXQiLCBtYWluID0gIk1hZHJpZCIsIGNleC5sYWIgPSAwLjc1KQphYmxpbmUoYT0wLCBiPTEsIGNvbD0iYmx1ZSIpCmdyaWQoMTAsMTApCgojIENsb3NpbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKZGV2Lm9mZigpCmBgYAoKIyMjIENvbXB1dGUgdGhlIFJNU0UgZm9yIHRoZSBwcmVkaWN0aW9ucwoKYGBge3J9CnJvb3Rtc2UgPC0gcm1zZShldmFsTWFkcmlkJGVuZXJneV9kZW1hbmQsIHByZWRpY3QuZXZhbCkKcm9vdG1zZSA8LSBybXNlKGV2YWxkYXRhJGVuZXJneV9kZW1hbmQsIHByZWRpY3QuZXZhbCkKcHJpbnQocGFzdGUoIlRoZSByb290IG1lYW4gc3F1YXJlIGVycm9yIG9mIHRoZSBwcmVkaWN0ZWQgdnMgYWN0dWFsIixucm93KGV2YWxkYXRhKSwiaG91cmx5IGVuZXJneV9kYXRhIHBvaW50cyBmb3IgTWFkcmlkIG92ZXIgdGhlIDIwMTUtMjAxOCB0aW1lc3BhbiBpcyBhbiBlbmVyZ3kgZGVtYW5kIGRpZmZlcmVuY2Ugb2YiLHJvdW5kKHJvb3Rtc2UsMiksIk1XaC4iKSkKY2F0KCJcbiIpCmRlbWFuZGNvbXBkZiA8LSBkYXRhLmZyYW1lKCJBY3R1YWwgKE1XaCkiID0gZXZhbE1hZHJpZCRlbmVyZ3lfZGVtYW5kLCJQcmVkaWN0ZWQgKE1XaCkiID0gcHJlZGljdC5ldmFsKQpoZWFkKGRlbWFuZGNvbXBkZiw0TCkKY2F0KCJcbiIpCnByaW50KHBhc3RlKCJGb3IgZXhhbXBsZSwgdGhlIGZpcnN0IGFjdHVhbCBkZW1hbmQgdmFsdWUgaXMiLHJvdW5kKGV2YWxkYXRhJGVuZXJneV9kZW1hbmRbMV0sMiksIk1XaCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgcHJlZGljdGVkIHZhbHVlIGlzIixyb3VuZChwcmVkaWN0LmV2YWxbMV0sMiksIk1XaC4gVGhlIHBlcmNlbnQgZXJyb3IgYmV0d2VlbiB0aGlzIHByZWRpY3RlZCB2YWx1ZSByZWxhdGl2ZSB0byB0aGUgYWN0dWFsIHZhbHVlIGlzIixyb3VuZCgoMTAwKihwcmVkaWN0LmV2YWxbMV0gLSBldmFsZGF0YSRlbmVyZ3lfZGVtYW5kWzFdKS9ldmFsZGF0YSRlbmVyZ3lfZGVtYW5kWzFdKSwyKSwiJS4iKSkKY2F0KCJcbiIpCmRpZmZwY3QgPC0gYygoYWJzKGV2YWxkYXRhJGVuZXJneV9kZW1hbmQgLSBwcmVkaWN0LmV2YWwpKS9ldmFsZGF0YSRlbmVyZ3lfZGVtYW5kKQpwcmludChwYXN0ZSgiVGhlIG1lYW4gZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSIsbnJvdyhldmFsZGF0YSksImV2YWx1YXRpb24gZGF0YSBzZXQgYWN0dWFsIGFuZCBtb2RlbCBwcmVkaWN0ZWQgdmFsdWVzIGlzIixyb3VuZCgxMDAqbWVhbihkaWZmcGN0KSwyKSwiJS4iKSkKYGBgCgojIyBDb21wdXRlIGFuZCBhbmFseXplIEhhdCBNYXRyaXggZm9yIEV2YWx1YXRpb24gZGF0YSBzZXQKT2J0YWluIFggbWF0cml4IGZyb20gdGhlIG1vZGVsCmBgYHtyfQpQcmVkaWN0b3JzIDwtcm93bmFtZXMoc3VtbWFyeSh3b3JrRmluYWwpJGNvZWZmaWNpZW50c1ssXSkKd29ya3ByZWRpY3RvcnMgPC0gYXMuZGF0YS5mcmFtZShQcmVkaWN0b3JzKQpYIDwtIG1vZGVsLm1hdHJpeCh3b3JrRmluYWwpCmNsYXNzKFgpCmNhdCgiXG4iKQpkaW0oWCkKY2F0KCJcbiIpCmhlYWQoWCwgNEwpCmBgYAoKQ3JlYXRlIHhfbmV3IG1hdHJpeCB1c2luZyB0aGUgdmFsaWRhdGlvbiBkYXRhLiAgTm90ZSB0aGF0IHRoZSBwdXJwb3NlIG9mIHJ1bm5pbmcgdGhlIGV2YWx1YXRpb24gcmVncmVzc2lvbiBoZXJlIGlzIHRvIGV4dHJhY3QgYWxsIG9mIHRoZSBjb3JyZWN0IHByZWRpY3RvciBjb2VmZmljaWVudHMuCmBgYHtyfQpldmFsRmluYWwgPC0gbG0oZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyAKICAgIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyAKICAgIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gZXZhbE1hZHJpZCkKUHJlZGljdG9ycyA8LXJvd25hbWVzKHN1bW1hcnkoZXZhbEZpbmFsKSRjb2VmZmljaWVudHNbLF0pCmV2YWxwcmVkaWN0b3JzIDwtIGFzLmRhdGEuZnJhbWUoUHJlZGljdG9ycykKeF9uZXcxIDwtIG1vZGVsLm1hdHJpeChldmFsRmluYWwpCmNhdCgiXG4iKQpkaW0oeF9uZXcxKQpjYXQoIlxuIikKI0NoZWNrIGlmIHRoZXJlJ3MgYSBwcmVkaWN0b3IgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB3b3JrIGFuZCBldmFsIG1hdHJpY2VzCndvcmtldmFsY29tcGFyZSA8LWFudGlfam9pbih3b3JrcHJlZGljdG9ycyxldmFscHJlZGljdG9ycykKY2F0KCJcbiIpCiNJZiB3b3JrZXZhbGNvbXBhcmUgaXMgJ05vIGRhdGEgYXZhaWxhYmxlIGluIHRhYmxlJywgcnVuIHRoZSBuZXh0IGxpbmUgb2YgY29kZSBhbmQgc2tpcCB0byBsaW5lIDgxNgp4X25ldyA8LSB4X25ldzEKI090aGVyd2lzZSB3aGVuIHdvcmtldmFsY29tcGFyZSBpcyBub3QgJ05vIGRhdGEgYXZhaWxhYmxlIGluIHRhYmxlJywgcnVuIHRoZSBmb2xsb3dpbmcgbGluZXMgb2YgY29kZSBhZnRlciBpZGVudGlmeWluZyB3aGljaCBjb2x1bW4gaXMgbWlzc2luZyBiZXR3ZWVuIFggYW5kIHhfbmV3MSAobm8gZGlzY3JlcGFuY3kgd2FzIGZvdW5kIGJldHdlZW4gd29ya01hZHJpZCBidXQgbm90IGluIGV2YWxNYWRyaWQpLgojeF9uZXdfIDwtYXMuZGF0YS5mcmFtZShjYmluZCh4X25ldzEscmVwKDAsbnJvdyh4X25ldzEpKSkpCiNkdW1teV94bmV3PC0geF9uZXdfICU+JSByZWxvY2F0ZShWMjEsLmJlZm9yZSA9IDE2KSAlPiUgcmVuYW1lKCJmYWN0b3Iod2VhdGhlcl9tYWluKTQiID0gVjIxKQojeF9uZXcgPC0gYXMubWF0cml4KGR1bW15X3huZXcpICAKY2F0KCJcbiIpCmNsYXNzKHhfbmV3KQpjYXQoIlxuIikKZGltKHhfbmV3KQpjYXQoIlxuIikKaGVhZCh4X25ldywgNEwpCmBgYAoKQ29tcHV0ZSB0aGUgaGF0IHZhbHVlcyBmb3IgdGhlIHZhbGlkYXRpb24gZGF0YQpgYGB7cn0KaF9uZXdfbWlkIDwtIHNvbHZlKHQoWCklKiVYKQpkaW0oaF9uZXdfbWlkKQpoX25ldyA8LSB4X25ldyUqJWhfbmV3X21pZCUqJXQoeF9uZXcpCmRpbShoX25ldykKYGBgCgpQbG90IHRoZSBoYXR2YWx1ZXMgZm9yIHRoZSBkYXRhIGluIHRoZSBldmFsdWF0aW9uIHNldApgYGB7ciBmaWcuaGVpZ2h0PTR9CiNjdXRsZXYyID0gKDIqbGVuZ3RoKGNvZWZmaWNpZW50cyhldmFsRmluYWwpKSkvbnJvdyhldmFsTWFkcmlkKQojIENvdW50IGFuZCBhc3Nlc3MgdGhlIG51bWJlciBvZiBsZXZlcmFnZSB2YWx1ZXMgPiBjdXQtb2ZmCnBvdG91dGxpZXIyIDwtIHN1bShkaWFnKGhfbmV3KSA+IGN1dGxldiwgbmEucm09VFJVRSkKdG90Y291bnQyID0gbnJvdyhldmFsTWFkcmlkKQpwbG90KGRpYWcoaF9uZXcpLCB0eXBlID0gImgiLCB5bGFiID0gIkxldmVyYWdlIGZvciB0aGUgdmFsaWRhdGlvbiBkYXRhIHNldCIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCBtYWluID0gIk1hZHJpZCIpCmFibGluZShoPWN1dGxldiwgY29sPSJyZWQiLCBsdHk9MikKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2YgbGV2ZXJhZ2UgcG9pbnRzIHRoYXQgYXJlIHBvdGVudGlhbCBvdXRsaWVycyBpcyIsIHBvdG91dGxpZXIyLCIuICBUaGlzIGlzIiwgcm91bmQoMTAwKnBvdG91dGxpZXIyL3RvdGNvdW50MiwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHByZWRpY3RlZCB2YWx1ZXMsICgiLCB0b3Rjb3VudDIsIikgdGh1cyBub3QgbWF0ZXJpYWwuIikpCmBgYAoKU2lkZS1ieS1zaWRlIGNvbXBhcmlzaW9uCmBgYHtyIGZpZy5oZWlnaHQ9NH0KcGFyKG1mcm93PWMoMSwyKSkKcGxvdChsZXYsIHR5cGU9ImgiLCB5bGltID0gYygwLCAyKmN1dGxldiksIHlsYWI9IkxldmVyYWdlIGZvciB0aGUgd29ya2luZyBkYXRhIHNldCIsIG1haW4gPSAiTWFkcmlkIikKYWJsaW5lKGggPSBjdXRsZXYsIGNvbCA9ICJyZWQiLCBsdHk9MikKcGxvdChkaWFnKGhfbmV3KSwgdHlwZSA9ICJoIiwgeWxpbSA9IGMoMCwyKmN1dGxldiksIHlsYWIgPSAiTGV2ZXJhZ2UgZm9yIHRoZSBldmFsdWF0aW9uIGRhdGEgc2V0IiwgbWFpbiA9ICJNYWRyaWQiKQphYmxpbmUoaD1jdXRsZXYsIGNvbD0icmVkIiwgbHR5PTIpCnByaW50KHBhc3RlKCJUaGUgbGV2ZXJhZ2UgY3V0b2ZmIGV4Y2VlZGVuY2VzIGluIHRoZSBldmFsdWF0aW9uIHNldCBpcyIscm91bmQoMTAwKnBvdG91dGxpZXIyL3RvdGNvdW50MiwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIG9ic2VydmVkIHZhbHVlcyAoIiwgdG90Y291bnQyLCIpIGFuZCBpcyBjb21wYXJhYmxlIHRvIHRoYXQgb2YgdGhlIHdvcmtpbmcgc2V0LCIsIHJvdW5kKDEwMCpwb3RvdXRsaWVyL3RvdGNvdW50LDIpLCIlIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdGVkIHZhbHVlcyAoIiwgdG90Y291bnQsIikuIikpCmBgYAoKUHJlcGFyZSByZXBvcnQgZmlndXJlCmBgYHtyfQojIE9wZW5pbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKIyBDdXN0b21pemluZyB0aGUgb3V0cHV0CnBkZigiTWFkcmlkTGV2Q29tcC5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDE0LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKICAgCnBhcihtZnJvdz1jKDEsMikpCnBsb3QobGV2LCB0eXBlPSJoIiwgeWxhYiA9ICJMZXZlcmFnZSIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCB5YXh0ID0gJ24nLCBtYWluID0gIk1hZHJpZCBXb3JraW5nIERhdGEiLCByZXMgPTYwMCkKYWJsaW5lKGggPSBjdXRsZXYsIGNvbCA9ICJyZWQiLCBsdHk9MikKYXhpcyhzaWRlID0gMiwgYXQ9c2VxKDAsMipyb3VuZChjdXRsZXYsNSksYnk9cm91bmQoY3V0bGV2LDUpKSkKCnBsb3QoZGlhZyhoX25ldyksIHR5cGUgPSAiaCIsIHlsYWIgPSAiIiwgeWxpbSA9IGMoMCwyKmN1dGxldiksIHlheHQgPSAnbicsIG1haW4gPSAiTWFkcmlkIEV2YWx1YXRpb24gRGF0YSIpCmFibGluZShoPWN1dGxldiwgY29sPSJyZWQiLCBsdHk9MikKYXhpcyhzaWRlID0gMiwgYXQ9c2VxKDAsMipyb3VuZChjdXRsZXYsNSksYnk9cm91bmQoY3V0bGV2LDUpKSkKCiMgQ2xvc2luZyB0aGUgZ3JhcGhpY2FsIGRldmljZQpkZXYub2ZmKCkKYGBgCgojIyBTZXZpbGxlIE1vZGVsCgpgYGB7cn0KZXZhbGRhdGE8LXJlYWQuY3N2KCdkYXRhL2V2YWx1YXRpb25fU2V2aWxsZS5jc3YnKQp3b3JrZGF0YTwtcmVhZC5jc3YoJ2RhdGEvd29ya2luZ19TZXZpbGxlLmNzdicpCiNyZW1vdmUgaW5kZXgsIHRpbWVfSUQgYW5kIHNub3dfZHVyYXRpb24gY29sdW1ucyBmcm9tICJldmFsIiBhbmFseXNpcyBkYXRhZnJhbWUKZXZhbFNldmlsbGU8LWV2YWxkYXRhW2MoLTEsLTIsLTExKV0gCiNyZW1vdmUgaW5kZXgsIHRpbWVfSUQgYW5kIHNub3dfZHVyYXRpb24gY29sdW1ucyBmcm9tICJ3b3JrIiBhbmFseXNpcyBkYXRhZnJhbWUKd29ya1NldmlsbGU8LXdvcmtkYXRhW2MoLTEsLTIsLTExKV0gCmhlYWQod29ya1NldmlsbGUsIDRMKQpgYGAKCgpgYGB7ciwsZmlnLmhlaWdodD01fQojIENvcnJlbGF0aW9uIG1hdHJpeCBmb3IgbnVtZXJpYyBwcmVkaWN0b3JzIG9ubHkgKGNhdGVnb3JpY2FsIHByZWRpY3RvcnMgZXhjbHVkZWQpCndvcmtjb3I8LXdvcmtTZXZpbGxlW2MoLTk6LTEyKSBdCmdncGFpcnMod29ya2NvcikKY29yKHdvcmtTZXZpbGxlKQpgYGAKCgpgYGB7cixmaWcuaGVpZ2h0PTR9CmNvcnJwbG90KGNvcih3b3JrY29yKSwgbWV0aG9kPSJjb2xvciIsIHR5cGU9ImZ1bGwiLCBhZGRDb2VmLmNvbCA9ICJyZWQiLCB0bC5jb2w9ImJsYWNrIixudW1iZXIuY2V4ID0gMC43NSkKYGBgCgoKYGBge3J9CndvcmtNb2RlbFNldmlsbGUxIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzIgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya1NldmlsbGUpCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CnZpZih3b3JrTW9kZWxTZXZpbGxlMSkKYGBgCgoKYGBge3J9CndvcmtNb2RlbFNldmlsbGVGdWxsIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya1NldmlsbGUpCnZpZih3b3JrTW9kZWxTZXZpbGxlRnVsbCkKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9NX0KIyB0byBvYnRhaW4gcmVzaWR1YWxzCnJlcy5mdWxsbW9kZWwxIDwtIHJlc2lkdWFscyh3b3JrTW9kZWxTZXZpbGxlRnVsbCkgCiMgdG8gb2J0YWluIHN0YW5kYXJkaXplZCByZXNpZHVhbHMKc3RkLnJlcy5mdWxsbW9kZWwxIDwtIHJzdGFuZGFyZCh3b3JrTW9kZWxTZXZpbGxlRnVsbCkgCiMgdG8gb2J0YWluIGZpdHRlZC9wcmVkaWN0ZWQgdmFsdWVzCnByZWQuZnVsbG1vZGVsMSA8LSBmaXR0ZWQudmFsdWVzKHdvcmtNb2RlbFNldmlsbGVGdWxsKSAKcGFyKG1mcm93PWMoMSwxKSkKcXFub3JtKHkgPSBzdGQucmVzLmZ1bGxtb2RlbDEsIG1haW4gPSAiIE5vcm1hbCBRLVEgUGxvdCAiLAogICAgICAgeGxhYiA9ICJUaGVvcmV0aWNhbCBRdWFudGlsZXMiLCB5bGFiID0gIlNhbXBsZSBRdWFudGlsZXMiKQpxcWxpbmUoeSA9IHN0ZC5yZXMuZnVsbG1vZGVsMSkKI3Jlc2lkdWFsIHBsb3QKcmVzcGxvdGRhdGExIDwtIGRhdGEuZnJhbWUoc3RkLnJlcy5mdWxsbW9kZWwxLCBwcmVkLmZ1bGxtb2RlbDEpCnJlc2JmMSA8LSBsbShzdGQucmVzLmZ1bGxtb2RlbDEgfiBwcmVkLmZ1bGxtb2RlbDEsIGRhdGEgPSByZXNwbG90ZGF0YTEpCnBsb3QoeCA9IHByZWQuZnVsbG1vZGVsMSwgeSA9IHN0ZC5yZXMuZnVsbG1vZGVsMSwgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgUmVzaWR1YWxzIiwgeGxhYiA9ICJQcmVkaWN0ZWQgVmFsdWVzIiwgbWFpbiA9ICJSZXNpZHVhbHMgUGxvdCIsIGNvbCA9IGlmZWxzZShzdGQucmVzLmZ1bGxtb2RlbDEgPCAtMywicmVkIixpZmVsc2Uoc3RkLnJlcy5mdWxsbW9kZWwxID4gMywicmVkIiwiYmxhY2siKSkpCmFibGluZShoID0gMCwgY29sPSJibHVlIiwgbHR5PTEpCmFibGluZShyZXNiZjEsIGNvbD0icmVkIiwgbHR5PTMpCmFibGluZShoID0gMywgY29sPSJncmVlbiIsIGx0eT0zKQphYmxpbmUoaD0tMywgY29sPSJncmVlbiIsIGx0eT0zKQpsZWdlbmQoImJvdHRvbWxlZnQiLCBsZWdlbmQ9YygiQmVzdCBmaXQgbGluZSBvZiBzdGFuZGFyZGl6ZWQgcmVzaWR1YWxzIiwgIkhvcml6b250YWwgbGluZSB5ID0gMC4wIiwgIkhvcml6b250YWwgbGluZSwgeSA9ICsvLSAzIiksIGZpbGwgPSBjKCJyZWQiLCJibHVlIiwiZ3JlZW4iKSwgY2V4ID0gMC41KQpgYGAKCmBgYHtyfQpzdW1tYXJ5KHdvcmtNb2RlbFNldmlsbGVGdWxsKQpkZjwtIHdvcmtNb2RlbFNldmlsbGVGdWxsICU+JSAKICB0aWR5KCkKI0dldCB0aGUgdmFyaWFibGVzIHdoaWNoIHRoZXJlIHAtdmFsdWUgbGFyZ2VyIHRoYW4gMC4wNSAKZGYlPiUKICBmaWx0ZXIoZGYkcC52YWx1ZT4wLjA1KQp3b3JrTW9kZWxTZXZpbGxlUHZhbHVlIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrU2V2aWxsZSkKc3VtbWFyeSh3b3JrTW9kZWxTZXZpbGxlUHZhbHVlKQpgYGAKCisgcC12YWx1ZSBhcHByb2FjaAoKYGBge3J9CmFub3ZhKHdvcmtNb2RlbFNldmlsbGVQdmFsdWUsIHdvcmtNb2RlbFNldmlsbGVGdWxsKQpgYGAKCisgQmVzdCBzdWJzZXQgc2VsZWN0aW9uIG1ldGhvZAoKYGBge3IsIGZpZy5oZWlnaHQ9NX0KYmVzdGZpdHMgPC0gcmVnc3Vic2V0cyhlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtTZXZpbGxlLCBuYmVzdCA9IDEpCnBsb3QoYmVzdGZpdHMsIHNjYWxlPSJhZGpyMiIpCmBgYAoKYGBge3J9CndvcmtNb2RlbFNldmlsbGVCZXN0Zml0PC1sbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHdpbmRfc3BlZWQgKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3RvcihzZWFzb24pICwgZGF0YSA9IHdvcmtTZXZpbGxlKQpzdW1tYXJ5KHdvcmtNb2RlbFNldmlsbGVCZXN0Zml0KQphbm92YSh3b3JrTW9kZWxTZXZpbGxlQmVzdGZpdCwgd29ya01vZGVsU2V2aWxsZVB2YWx1ZSkKYGBgCgorIEFJQyB3aXRoIGZvcndhcmQgc2VsZWN0aW9uIGFwcHJvYWNoIHRlc3QgbWV0aG9kCgpgYGB7cn0Kd29ya051bGxNb2RlbDwtIGxtKGVuZXJneV9kZW1hbmQgfiAxLCBkYXRhID0gd29ya1NldmlsbGUpCnN0ZXAud29ya2luZyA8LSBzdGVwQUlDKHdvcmtOdWxsTW9kZWwsIHNjb3BlID0gbGlzdChsb3dlciA9IHdvcmtOdWxsTW9kZWwsCnVwcGVyID0gd29ya01vZGVsU2V2aWxsZUZ1bGwpLCBkaXJlY3Rpb24gPSAiZm9yd2FyZCIsIHRyYWNlPUZBTFNFKQpzdW1tYXJ5KHN0ZXAud29ya2luZykKYGBgCgorIENyb3NzIFZhbGlkYXRpb24gdGVzdCBtZXRob2QKCmBgYHtyLHdhcm5pbmc9RkFMU0V9CiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eSBpbiBDVgpzZXQuc2VlZCgxMjMpCiMgU2V0IHVwIHJlcGVhdGVkIGstZm9sZCBjcm9zcy12YWxpZGF0aW9uLCB3aXRoIGs9MTAKdHJhaW4uY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTApCiMgVHJhaW4gdGhlIG1vZGVsCnN0ZXAuY3Yud29yayA8LSB0cmFpbihlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtTZXZpbGxlLCBtZXRob2QgPSAibGVhcEJhY2t3YXJkIiwKdHVuZUdyaWQgPSBkYXRhLmZyYW1lKG52bWF4ID0gMToxMSksCnRyQ29udHJvbCA9IHRyYWluLmNvbnRyb2wpCnN0ZXAuY3Yud29yayRyZXN1bHRzCnN1bW1hcnkoc3RlcC5jdi53b3JrJGZpbmFsTW9kZWwpCmBgYAoKUmVkdWNlZCBtb2RlbCBzYW1lIGFzIFAtdmFsdWUgYXBwcm9hY2gKCisgT0xTIG1ldGhvZAoKYGBge3IgZmlnLmhlaWdodD00fQojTW9kZWxPTFMgPC0gb2xzX3N0ZXBfYWxsX3Bvc3NpYmxlKHdvcmtNb2RlbFNldmlsbGVGdWxsKQojY2F0KCJcbiIpCiNwbG90KHggPSBNb2RlbE9MUyRuLCB5ID0gTW9kZWxPTFMkYWRqcikKI3Bsb3QoeCA9IE1vZGVsT0xTJG4sIHkgPSBNb2RlbE9MUyRhaWMpCiNPTFNzdW1tYXJ5IDwtIE1vZGVsT0xTICU+JSBncm91cF9ieShuKSAlPiUgIGZpbHRlcihhZGpyID09IG1heChhZGpyKSkKYGBgCgpTYW1lIHJlZHVjZWQgbW9kZWwgYXMgUC1WYWx1ZSBhcHByb2FjaCAodGh1cyBjb21tZW50aW5nIG91dCBnaXZlbiBwcm9jZXNzaW5nIHRpbWUpCgojIyBTZWxlY3QgdGhlIGZpbmFsIG1vZGVsCgpNb2RlbCBhZGp1c3RlZCBSLXNxdWFyZSBmcm9tIGFib3ZlIG1ldGhvZHMgKHRvIGZvdXIgc2lnbmlmaWNhbnQgZGlnaXRzKQoKQSkgRnVsbDogMC45MTk5CgpCKSBQLXZhbHVlOiAwLjkxOTkKCkMpIEJlc3RmaXRzOiAwLjkxODkKCkQpIEFJQyBNZXRob2Q6IDAuOTE5OQoKRSkgQ3Jvc3MtdmFsaWRhdGlvbjogMC45MTk5CgpGaW5hbCBtb2RlbCBpcyB0aGUgd29ya01vZGVsU2V2aWxsZVB2YWx1ZSB3aXRoIGhpZ2hlc3QgQWRqdXN0ZWQgUi1zcXVhcmUgYW5kIGZld2VzdCBwcmVkaWN0b3JzLgoKYGBge3J9Ck1ldGhvZCA8LSBjKCdGdWxsJywnUC12YWx1ZScsICdCZXN0Rml0cycsICdBSUMgRm9yd2FyZCcsICdDcm9zcyBWYWxpZGF0aW9uJykKVmFyaWFibGVzIDwtIGMoMTIsMTEsOCwxMiwxMSkKQ29lZmZpY2llbnRzIDwtIGMoMjQsMjMsMTAsMjQsMjMpCkFkanVzdGVkX1IyIDwtIGMoMC45MTk5MjY0LDAuOTE5OTIwOCwwLjkxODk0NTcsMC45MTk5MjY0LDAuOTE5OTIwOCkKU2V2aWxsZVRhYmxlPC1kYXRhLmZyYW1lKE1ldGhvZCxWYXJpYWJsZXMsQ29lZmZpY2llbnRzLEFkanVzdGVkX1IyKQpgYGAKCiMjIyBCdWlsZCB0aGUgRmluYWwgTW9kZWwKCmBgYHtyfQp3b3JrRmluYWwgPC0gd29ya01vZGVsU2V2aWxsZVB2YWx1ZQpgYGAKCgojIyMgQ2hlY2sgdGhlIGxpbmVhcml0eSwgaGV0ZXJvc2NlZGFzdGljaXR5LCBvdXRsaWVyIGFuZCBub3JtYWwgYXNzdW1wdGlvbgoKYGBge3IsIGZpZy5oZWlnaHQ9NH0KcGFyKG1mcm93PWMoMiwyKSkKI1Jlc2lkdWFsIFBsb3QKcGxvdCh4PWZpdHRlZCh3b3JrRmluYWwpLCB5PXN0dWRyZXMod29ya0ZpbmFsKSwgeGxhYiA9ICJGaXR0ZWQgVmFsdWVzIiwKeWxhYiA9ICJTdHVkZW50aXplZCBSZXNpZHVhbHMiLCBtYWluPSdSZXNpZHVhbHMgUGxvdCcsIGNvbCA9IGlmZWxzZShzdHVkcmVzKHdvcmtGaW5hbCkgPCAtMywicmVkIixpZmVsc2Uoc3R1ZHJlcyh3b3JrRmluYWwpID4gMywicmVkIiwiYmxhY2siKSkpCmFibGluZShoPS0zLCBjb2w9InJlZCIsIGx0eT0yKQphYmxpbmUoaD0zLCBjb2w9InJlZCIsIGx0eT0yKQojbm9ybSB0ZXN0CnN0ZC5yZXMgPC0gcnN0YW5kYXJkKHdvcmtGaW5hbCkKYWQudGVzdChzdGQucmVzKQpxcW5vcm0oc3R1ZHJlcyh3b3JrRmluYWwpLCBwY2ggPSAxLCBmcmFtZSA9IEZBTFNFLCBtYWluPWV4cHJlc3Npb24oIkFEIHRlc3QgPTIuMioxMCJeLTE2KSkKcXFsaW5lKHN0dWRyZXMod29ya0ZpbmFsKSwgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDIpCiNsZXZlcmFnZSAKbGV2PC1oYXR2YWx1ZXMod29ya0ZpbmFsKQpjdXRsZXYgPSAoMipsZW5ndGgoY29lZmZpY2llbnRzKHdvcmtGaW5hbCkpKS9ucm93KHdvcmtTZXZpbGxlKQojIENvdW50IGFuZCBhc3Nlc3MgdGhlIG51bWJlciBvZiBsZXZlcmFnZSB2YWx1ZXMgPiBjdXQtb2ZmCnBvdG91dGxpZXIgPC0gc3VtKGxldiA+IGN1dGxldiwgbmEucm09VFJVRSkKdG90Y291bnQgPSBsZW5ndGgoZml0dGVkKHdvcmtGaW5hbCkpCnByaW50KHBhc3RlKCJUaGUgbnVtYmVyIG9mIGxldmVyYWdlIHBvaW50cyB0aGF0IGFyZSBwb3RlbnRpYWwgb3V0bGllcnMgaXMiLCBwb3RvdXRsaWVyLCIuICBUaGlzIGlzIiwgcm91bmQoMTAwKnBvdG91dGxpZXIvdG90Y291bnQsMiksIiUgb2YgdGhlIHRvdGFsIG51bWJlciBvZiBwcmVkaWN0ZWQgdmFsdWVzLCAoIiwgdG90Y291bnQsIikgdGh1cyBub3QgbWF0ZXJpYWwuIikpCiNiYXJwbG90KGxldiwgeWxpbSA9IGMoMCwgMipjdXRsZXYpKQpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIHRoZSB3b3JraW5nIHNldCIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCiNjb29rIGRpc3RhbmNlCmNvb2tkaXN0PC1jb29rcy5kaXN0YW5jZSh3b3JrRmluYWwpCiNiYXJwbG90KGNvb2tkaXN0LCB5bGltPWMoMCwxLjAxKSwgbWFpbiA9ICJDb29rJ3MgRGlzdGFuY2UgcGxvdCIpCiNhYmxpbmUoaCA9IDEsIGNvbCA9ICJyZWQiKQpnZ3Bsb3QoYXMuZGF0YS5mcmFtZShjb29rZGlzdCksYWVzKHg9MTpucm93KGFzLmRhdGEuZnJhbWUoY29va2Rpc3QpKSx5PWNvb2tkaXN0KSkgKwogIGdlb21fbGluZSgpICsgCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDEsY29sb3IgPSAicmVkIikpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMS4xKSkrCiAgc2NhbGVfeV9icmVhayhicmVha3MgPSBjKDAuMDMsMC45KSwgc2NhbGVzID0wLjEpICsKICBndWlkZXMoY29sb3IgPSAibm9uZSIpICsKICBsYWJzKHRpdGxlPSJDb29rJ3MgRGlzdGFuY2UiLAogICAgICAgIHggPSJJbmRleCIsIHkgPSAiIikKYGBgCgpgYGB7cn0KIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoIlNldmlsbGVBc3N1bXB0aW9uQ2hlY2sxLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCiAgIAojIHBhcihtZnJvdz1jKDIsMikpCiNyZXNpZHVhbHMgc2NhdHRlciBwbG90CnBsb3QoeD1maXR0ZWQod29ya0ZpbmFsKSwgeT1zdHVkcmVzKHdvcmtGaW5hbCksIHhsYWIgPSAiRml0dGVkIFZhbHVlcyIsCnlsYWIgPSAiU3R1ZGVudGl6ZWQgUmVzaWR1YWxzIiwgbWFpbj0nUmVzaWR1YWxzIFBsb3QnLCBjb2wgPSBpZmVsc2Uoc3R1ZHJlcyh3b3JrRmluYWwpIDwgLTMsInJlZCIsaWZlbHNlKHN0dWRyZXMod29ya0ZpbmFsKSA+IDMsInJlZCIsImJsYWNrIikpKQphYmxpbmUoaD0tMywgY29sPSJyZWQiLCBsdHk9MikKYWJsaW5lKGg9MywgY29sPSJyZWQiLCBsdHk9MikKZ3JpZCgxMCwxMCkKZGV2Lm9mZigpCgoKI25vcm1hbGl0eSBwbG90CnBkZigiU2V2aWxsZUFzc3VtcHRpb25DaGVjazIucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKcXFub3JtKHN0dWRyZXMod29ya0ZpbmFsKSwgcGNoID0gMSwgZnJhbWUgPSBGQUxTRSwgbWFpbj1leHByZXNzaW9uKCJBRCB0ZXN0ID0yLjIqMTAiXi0xNikpCnFxbGluZShzdHVkcmVzKHdvcmtGaW5hbCksIGNvbCA9ICJzdGVlbGJsdWUiLCBsd2QgPSAyKQpncmlkKDEwLDEwKQpkZXYub2ZmKCkKCgojbGV2ZXJhZ2UgcGxvdApwZGYoIlNldmlsbGVBc3N1bXB0aW9uQ2hlY2szLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCgpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIEJhcmNlbG9uYSB3b3JraW5nIHNldCIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKCiNDb29rJ3MgZGlzdGFuY2UgcGxvdApwZGYoIlNldmlsbGVBc3N1bXB0aW9uQ2hlY2s0LnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCmdncGxvdChhcy5kYXRhLmZyYW1lKGNvb2tkaXN0KSxhZXMoeD0xOm5yb3coYXMuZGF0YS5mcmFtZShjb29rZGlzdCkpLHk9Y29va2Rpc3QpKSArCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMSxjb2xvciA9ICJyZWQiKSkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxLjEpKSsKICBzY2FsZV95X2JyZWFrKGJyZWFrcyA9IGMoMC4wMDUsMC45KSwgc2NhbGVzID0wLjEpICsKICBndWlkZXMoY29sb3IgPSAibm9uZSIpICsKICBsYWJzKHRpdGxlPSJDb29rJ3MgRGlzdGFuY2UiLAogICAgICAgIHggPSJJbmRleCIsIHkgPSAiIikKZGV2Lm9mZigpCgpgYGAKCiMjIyBVc2UgdGhlIGVzdGltYXRlZCBtb2RlbCB0byBwcmVkaWN0IHRoZSB2YWx1ZXMgaW4gdGhlIGV2YWx1YXRpb24gc2V0CgpgYGB7cn0KbmV3ZGF0YSA8LSBldmFsU2V2aWxsZVsgYygtMiwtMTMpXQpwcmVkaWN0LmV2YWwgPC0gcHJlZGljdCh3b3JrRmluYWwsIG5ld2RhdGEpCnBsb3QoeCA9IGV2YWxTZXZpbGxlJGVuZXJneV9kZW1hbmQsIHk9cHJlZGljdC5ldmFsLCB4bGFiPSJBY3R1YWwgZW5lcmd5IGRlbWFuZCBmb3IgdGhlIGV2YWx1YXRpb24gc2V0IiwKeWxhYj0iUHJlZGljdGVkIGVuZXJneSBkZW1hbmQgZm9yIHRoZSBldmFsdWF0aW9uIHNldCIsIG1haW4gPSAiU2V2aWxsZSIpCmFibGluZShhPTAsIGI9MSwgY29sPSJibHVlIikKZ3JpZCgxMCwxMCkKYGBgCgpQcmVwYXJlIHJlcG9ydCBmaWd1cmUKYGBge3IgZmlnLmhlaWdodD00fQojIE9wZW5pbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKIyBDdXN0b21pemluZyB0aGUgb3V0cHV0CnBkZigiU2V2aWxsZUV2YWxBY3RDb21wLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCiAgIApwYXIobWZyb3c9YygxLDEpKQpwbG90KHggPSBldmFsU2V2aWxsZSRlbmVyZ3lfZGVtYW5kLCB5PXByZWRpY3QuZXZhbCwgeGxhYj0iQWN0dWFsIGVuZXJneSBkZW1hbmQgZm9yIHRoZSBldmFsdWF0aW9uIHNldCIsCnlsYWI9IlByZWRpY3RlZCBlbmVyZ3kgZGVtYW5kIGZvciB0aGUgZXZhbHVhdGlvbiBzZXQiLCBtYWluID0gIlNldmlsbGUiLCBjZXgubGFiID0gMC43NSkKYWJsaW5lKGE9MCwgYj0xLCBjb2w9ImJsdWUiKQpncmlkKDEwLDEwKQoKIyBDbG9zaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCmRldi5vZmYoKQpgYGAKCiMjIyBDb21wdXRlIHRoZSBSTVNFIGZvciB0aGUgcHJlZGljdGlvbnMKCmBgYHtyfQpyb290bXNlIDwtIHJtc2UoZXZhbFNldmlsbGUkZW5lcmd5X2RlbWFuZCwgcHJlZGljdC5ldmFsKQpyb290bXNlIDwtIHJtc2UoZXZhbGRhdGEkZW5lcmd5X2RlbWFuZCwgcHJlZGljdC5ldmFsKQpwcmludChwYXN0ZSgiVGhlIHJvb3QgbWVhbiBzcXVhcmUgZXJyb3Igb2YgdGhlIHByZWRpY3RlZCB2cyBhY3R1YWwiLG5yb3coZXZhbGRhdGEpLCJob3VybHkgZW5lcmd5X2RhdGEgcG9pbnRzIGZvciBTZXZpbGxlIG92ZXIgdGhlIDIwMTUtMjAxOCB0aW1lc3BhbiBpcyBhbiBlbmVyZ3kgZGVtYW5kIGRpZmZlcmVuY2Ugb2YiLHJvdW5kKHJvb3Rtc2UsMiksIk1XaC4iKSkKY2F0KCJcbiIpCmRlbWFuZGNvbXBkZiA8LSBkYXRhLmZyYW1lKCJBY3R1YWwgKE1XaCkiID0gZXZhbFNldmlsbGUkZW5lcmd5X2RlbWFuZCwiUHJlZGljdGVkIChNV2gpIiA9IHByZWRpY3QuZXZhbCkKaGVhZChkZW1hbmRjb21wZGYsNEwpCmNhdCgiXG4iKQpwcmludChwYXN0ZSgiRm9yIGV4YW1wbGUsIHRoZSBmaXJzdCBhY3R1YWwgZGVtYW5kIHZhbHVlIGlzIixyb3VuZChldmFsZGF0YSRlbmVyZ3lfZGVtYW5kWzFdLDIpLCJNV2ggYW5kIHRoZSBjb3JyZXNwb25kaW5nIHByZWRpY3RlZCB2YWx1ZSBpcyIscm91bmQocHJlZGljdC5ldmFsWzFdLDIpLCJNV2guIFRoZSBwZXJjZW50IGVycm9yIGJldHdlZW4gdGhpcyBwcmVkaWN0ZWQgdmFsdWUgcmVsYXRpdmUgdG8gdGhlIGFjdHVhbCB2YWx1ZSBpcyIscm91bmQoKDEwMCoocHJlZGljdC5ldmFsWzFdIC0gZXZhbGRhdGEkZW5lcmd5X2RlbWFuZFsxXSkvZXZhbGRhdGEkZW5lcmd5X2RlbWFuZFsxXSksMiksIiUuIikpCmNhdCgiXG4iKQpkaWZmcGN0IDwtIGMoKGFicyhldmFsZGF0YSRlbmVyZ3lfZGVtYW5kIC0gcHJlZGljdC5ldmFsKSkvZXZhbGRhdGEkZW5lcmd5X2RlbWFuZCkKcHJpbnQocGFzdGUoIlRoZSBtZWFuIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUiLG5yb3coZXZhbGRhdGEpLCJldmFsdWF0aW9uIGRhdGEgc2V0IGFjdHVhbCBhbmQgbW9kZWwgcHJlZGljdGVkIHZhbHVlcyBpcyIscm91bmQoMTAwKm1lYW4oZGlmZnBjdCksMiksIiUuIikpCmBgYAoKIyMgQ29tcHV0ZSBhbmQgYW5hbHl6ZSBIYXQgTWF0cml4IGZvciBFdmFsdWF0aW9uIGRhdGEgc2V0Ck9idGFpbiBYIG1hdHJpeCBmcm9tIHRoZSBtb2RlbApgYGB7cn0KUHJlZGljdG9ycyA8LXJvd25hbWVzKHN1bW1hcnkod29ya0ZpbmFsKSRjb2VmZmljaWVudHNbLF0pCndvcmtwcmVkaWN0b3JzIDwtIGFzLmRhdGEuZnJhbWUoUHJlZGljdG9ycykKWCA8LSBtb2RlbC5tYXRyaXgod29ya0ZpbmFsKQpjbGFzcyhYKQpjYXQoIlxuIikKZGltKFgpCmNhdCgiXG4iKQpoZWFkKFgsIDRMKQpgYGAKCkNyZWF0ZSB4X25ldyBtYXRyaXggdXNpbmcgdGhlIHZhbGlkYXRpb24gZGF0YS4gIE5vdGUgdGhhdCB0aGUgcHVycG9zZSBvZiBydW5uaW5nIHRoZSBldmFsdWF0aW9uIHJlZ3Jlc3Npb24gaGVyZSBpcyB0byBleHRyYWN0IGFsbCBvZiB0aGUgY29ycmVjdCBwcmVkaWN0b3IgY29lZmZpY2llbnRzLgpgYGB7cn0KZXZhbEZpbmFsIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyAKICAgICAgICAgICAgICAgICAgd2luZF9zcGVlZCArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyAKICAgICAgICAgICAgICAgICAgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IGV2YWxTZXZpbGxlKQpQcmVkaWN0b3JzIDwtcm93bmFtZXMoc3VtbWFyeShldmFsRmluYWwpJGNvZWZmaWNpZW50c1ssXSkKZXZhbHByZWRpY3RvcnMgPC0gYXMuZGF0YS5mcmFtZShQcmVkaWN0b3JzKQp4X25ldzEgPC0gbW9kZWwubWF0cml4KGV2YWxGaW5hbCkKY2F0KCJcbiIpCmRpbSh4X25ldzEpCmNhdCgiXG4iKQojQ2hlY2sgaWYgdGhlcmUncyBhIHByZWRpY3RvciBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHdvcmsgYW5kIGV2YWwgbWF0cmljZXMKd29ya2V2YWxjb21wYXJlIDwtYW50aV9qb2luKHdvcmtwcmVkaWN0b3JzLGV2YWxwcmVkaWN0b3JzKQpjYXQoIlxuIikKI0lmIHdvcmtldmFsY29tcGFyZSBpcyAnTm8gZGF0YSBhdmFpbGFibGUgaW4gdGFibGUnLCBydW4gdGhlIG5leHQgbGluZSBvZiBjb2RlIGFuZCBza2lwIHRvIGxpbmUgMTU4MAojeF9uZXcgPC0geF9uZXcxCiNPdGhlcndpc2Ugd2hlbiB3b3JrZXZhbGNvbXBhcmUgaXMgbm90ICdObyBkYXRhIGF2YWlsYWJsZSBpbiB0YWJsZScsIHJ1biB0aGUgZm9sbG93aW5nIGxpbmVzIG9mIGNvZGUgYWZ0ZXIgaWRlbnRpZnlpbmcgd2hpY2ggY29sdW1uIGlzIG1pc3NpbmcgYmV0d2VlbiBYIGFuZCB4X25ldzEgKGZvciBTZXZpbGxlLCBpdCB3YXMgZm91bmQgdGhhdCAiZmFjdG9yKHdlYXRoZXJfbWFpbikxMSIgYXBwZWFycyBpbiB3b3JrU2V2aWxsZSBidXQgbm90IGluIGV2YWxTZXZpbGxlKS4KeF9uZXdfIDwtYXMuZGF0YS5mcmFtZShjYmluZCh4X25ldzEscmVwKDAsbnJvdyh4X25ldzEpKSkpCmR1bW15X3huZXc8LSB4X25ld18gJT4lIHJlbG9jYXRlKFYyMywuYmVmb3JlID0gMjIpICU+JSByZW5hbWUoImZhY3Rvcih3ZWF0aGVyX21haW4pMTEiID0gVjIzKQp4X25ldyA8LSBhcy5tYXRyaXgoZHVtbXlfeG5ldykgIApjYXQoIlxuIikKY2xhc3MoeF9uZXcpCmNhdCgiXG4iKQpkaW0oeF9uZXcpCmNhdCgiXG4iKQpoZWFkKHhfbmV3LCA0TCkKYGBgCgpDb21wdXRlIHRoZSBoYXQgdmFsdWVzIGZvciB0aGUgdmFsaWRhdGlvbiBkYXRhCmBgYHtyfQpoX25ld19taWQgPC0gc29sdmUodChYKSUqJVgpCmRpbShoX25ld19taWQpCmhfbmV3IDwtIHhfbmV3JSolaF9uZXdfbWlkJSoldCh4X25ldykKZGltKGhfbmV3KQpgYGAKClBsb3QgdGhlIGhhdHZhbHVlcyBmb3IgdGhlIGRhdGEgaW4gdGhlIGV2YWx1YXRpb24gc2V0CmBgYHtyIGZpZy5oZWlnaHQ9NH0KI2N1dGxldjIgPSAoMipsZW5ndGgoY29lZmZpY2llbnRzKGV2YWxGaW5hbCkpKS9ucm93KGV2YWxTZXZpbGxlKQojIENvdW50IGFuZCBhc3Nlc3MgdGhlIG51bWJlciBvZiBsZXZlcmFnZSB2YWx1ZXMgPiBjdXQtb2ZmCnBvdG91dGxpZXIyIDwtIHN1bShkaWFnKGhfbmV3KSA+IGN1dGxldiwgbmEucm09VFJVRSkKdG90Y291bnQyID0gbnJvdyhldmFsU2V2aWxsZSkKcGxvdChkaWFnKGhfbmV3KSwgdHlwZSA9ICJoIiwgeWxhYiA9ICJMZXZlcmFnZSBmb3IgdGhlIHZhbGlkYXRpb24gZGF0YSBzZXQiLCB5bGltID0gYygwLDIqY3V0bGV2KSwgbWFpbiA9ICJTZXZpbGxlIikKYWJsaW5lKGg9Y3V0bGV2LCBjb2w9InJlZCIsIGx0eT0yKQpwcmludChwYXN0ZSgiVGhlIG51bWJlciBvZiBsZXZlcmFnZSBwb2ludHMgdGhhdCBhcmUgcG90ZW50aWFsIG91dGxpZXJzIGlzIiwgcG90b3V0bGllcjIsIi4gIFRoaXMgaXMiLCByb3VuZCgxMDAqcG90b3V0bGllcjIvdG90Y291bnQyLDIpLCIlIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdGVkIHZhbHVlcywgKCIsIHRvdGNvdW50MiwiKSB0aHVzIG5vdCBtYXRlcmlhbC4iKSkKYGBgCgpTaWRlLWJ5LXNpZGUgY29tcGFyaXNpb24KYGBge3IgZmlnLmhlaWdodD00fQpwYXIobWZyb3c9YygxLDIpKQpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIHRoZSB3b3JraW5nIGRhdGEgc2V0IiwgbWFpbiA9ICJTZXZpbGxlIikKYWJsaW5lKGggPSBjdXRsZXYsIGNvbCA9ICJyZWQiLCBsdHk9MikKcGxvdChkaWFnKGhfbmV3KSwgdHlwZSA9ICJoIiwgeWxpbSA9IGMoMCwyKmN1dGxldiksIHlsYWIgPSAiTGV2ZXJhZ2UgZm9yIHRoZSBldmFsdWF0aW9uIGRhdGEgc2V0IiwgbWFpbiA9ICJTZXZpbGxlIikKYWJsaW5lKGg9Y3V0bGV2LCBjb2w9InJlZCIsIGx0eT0yKQpwcmludChwYXN0ZSgiVGhlIGxldmVyYWdlIGN1dG9mZiBleGNlZWRlbmNlcyBpbiB0aGUgZXZhbHVhdGlvbiBzZXQgaXMiLHJvdW5kKDEwMCpwb3RvdXRsaWVyMi90b3Rjb3VudDIsMiksIiUgb2YgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZlZCB2YWx1ZXMgKCIsIHRvdGNvdW50MiwiKSBhbmQgaXMgY29tcGFyYWJsZSB0byB0aGF0IG9mIHRoZSB3b3JraW5nIHNldCwiLCByb3VuZCgxMDAqcG90b3V0bGllci90b3Rjb3VudCwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHByZWRpY3RlZCB2YWx1ZXMgKCIsIHRvdGNvdW50LCIpLiIpKQpgYGAKClByZXBhcmUgcmVwb3J0IGZpZ3VyZQpgYGB7cn0KIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoIlNldmlsbGVMZXZDb21wLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gMTQsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbAogICAKcGFyKG1mcm93PWMoMSwyKSkKcGxvdChsZXYsIHR5cGU9ImgiLCB5bGFiID0gIkxldmVyYWdlIiwgeWxpbSA9IGMoMCwyKmN1dGxldiksIHlheHQgPSAnbicsIG1haW4gPSAiU2V2aWxsZSBXb3JraW5nIERhdGEiLCByZXMgPTYwMCkKYWJsaW5lKGggPSBjdXRsZXYsIGNvbCA9ICJyZWQiLCBsdHk9MikKYXhpcyhzaWRlID0gMiwgYXQ9c2VxKDAsMipyb3VuZChjdXRsZXYsNSksYnk9cm91bmQoY3V0bGV2LDUpKSkKCnBsb3QoZGlhZyhoX25ldyksIHR5cGUgPSAiaCIsIHlsYWIgPSAiIiwgeWxpbSA9IGMoMCwyKmN1dGxldiksIHlheHQgPSAnbicsIG1haW4gPSAiU2V2aWxsZSBFdmFsdWF0aW9uIERhdGEiKQphYmxpbmUoaD1jdXRsZXYsIGNvbD0icmVkIiwgbHR5PTIpCmF4aXMoc2lkZSA9IDIsIGF0PXNlcSgwLDIqcm91bmQoY3V0bGV2LDUpLGJ5PXJvdW5kKGN1dGxldiw1KSkpCgojIENsb3NpbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKZGV2Lm9mZigpCmBgYAoKIyMgVmFsZW5jaWEgTW9kZWwKCmBgYHtyfQpldmFsZGF0YTwtcmVhZC5jc3YoJ2RhdGEvZXZhbHVhdGlvbl9WYWxlbmNpYS5jc3YnKQp3b3JrZGF0YTwtcmVhZC5jc3YoJ2RhdGEvd29ya2luZ19WYWxlbmNpYS5jc3YnKQojcmVtb3ZlIGluZGV4IGFuZCB0aW1lX0lEIGNvbHVtbnMgZnJvbSAiZXZhbCIgYW5hbHlzaXMgZGF0YWZyYW1lCmV2YWxWYWxlbmNpYTwtZXZhbGRhdGFbYygtMSwtMildIAojcmVtb3ZlIGluZGV4IGFuZCB0aW1lX0lEIGNvbHVtbnMgZnJvbSAid29yayIgYW5hbHlzaXMgZGF0YWZyYW1lCndvcmtWYWxlbmNpYTwtd29ya2RhdGFbYygtMSwtMildIApoZWFkKHdvcmtWYWxlbmNpYSwgNEwpCmBgYAoKCmBgYHtyLCxmaWcuaGVpZ2h0PTV9CiMgQ29ycmVsYXRpb24gbWF0cml4IGZvciBudW1lcmljIHByZWRpY3RvcnMgb25seSAoY2F0ZWdvcmljYWwgcHJlZGljdG9ycyBleGNsdWRlZCkKd29ya2Nvcjwtd29ya1ZhbGVuY2lhW2MoLTEwOi0xMykgXQpnZ3BhaXJzKHdvcmtjb3IpCmNvcih3b3JrVmFsZW5jaWEpCmBgYAoKCmBgYHtyLGZpZy5oZWlnaHQ9NH0KY29ycnBsb3QoY29yKHdvcmtjb3IpLCBtZXRob2Q9ImNvbG9yIiwgdHlwZT0iZnVsbCIsIGFkZENvZWYuY29sID0gInJlZCIsIHRsLmNvbD0iYmxhY2siLG51bWJlci5jZXggPSAwLjc1KQpgYGAKCgpgYGB7cn0Kd29ya01vZGVsVmFsZW5jaWExIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzIgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgcHJlc3N1cmUgKyB3aW5kX3NwZWVkICsgcmFpbl9kdXJhdGlvbiArIHNub3dfZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtWYWxlbmNpYSkKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9NH0KdmlmKHdvcmtNb2RlbFZhbGVuY2lhMSkKYGBgCgoKYGBge3J9CndvcmtNb2RlbFZhbGVuY2lhRnVsbCA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBzbm93X2R1cmF0aW9uICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrVmFsZW5jaWEpCnZpZih3b3JrTW9kZWxWYWxlbmNpYUZ1bGwpCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTV9CiMgdG8gb2J0YWluIHJlc2lkdWFscwpyZXMuZnVsbG1vZGVsMSA8LSByZXNpZHVhbHMod29ya01vZGVsVmFsZW5jaWFGdWxsKSAKIyB0byBvYnRhaW4gc3RhbmRhcmRpemVkIHJlc2lkdWFscwpzdGQucmVzLmZ1bGxtb2RlbDEgPC0gcnN0YW5kYXJkKHdvcmtNb2RlbFZhbGVuY2lhRnVsbCkgCiMgdG8gb2J0YWluIGZpdHRlZC9wcmVkaWN0ZWQgdmFsdWVzCnByZWQuZnVsbG1vZGVsMSA8LSBmaXR0ZWQudmFsdWVzKHdvcmtNb2RlbFZhbGVuY2lhRnVsbCkgCnBhcihtZnJvdz1jKDEsMSkpCnFxbm9ybSh5ID0gc3RkLnJlcy5mdWxsbW9kZWwxLCBtYWluID0gIiBOb3JtYWwgUS1RIFBsb3QgIiwKICAgICAgIHhsYWIgPSAiVGhlb3JldGljYWwgUXVhbnRpbGVzIiwgeWxhYiA9ICJTYW1wbGUgUXVhbnRpbGVzIikKcXFsaW5lKHkgPSBzdGQucmVzLmZ1bGxtb2RlbDEpCiNyZXNpZHVhbCBwbG90CnJlc3Bsb3RkYXRhMSA8LSBkYXRhLmZyYW1lKHN0ZC5yZXMuZnVsbG1vZGVsMSwgcHJlZC5mdWxsbW9kZWwxKQpyZXNiZjEgPC0gbG0oc3RkLnJlcy5mdWxsbW9kZWwxIH4gcHJlZC5mdWxsbW9kZWwxLCBkYXRhID0gcmVzcGxvdGRhdGExKQpwbG90KHggPSBwcmVkLmZ1bGxtb2RlbDEsIHkgPSBzdGQucmVzLmZ1bGxtb2RlbDEsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIsIHhsYWIgPSAiUHJlZGljdGVkIFZhbHVlcyIsIG1haW4gPSAiUmVzaWR1YWxzIFBsb3QiLCBjb2wgPSBpZmVsc2Uoc3RkLnJlcy5mdWxsbW9kZWwxIDwgLTMsInJlZCIsaWZlbHNlKHN0ZC5yZXMuZnVsbG1vZGVsMSA+IDMsInJlZCIsImJsYWNrIikpKQphYmxpbmUoaCA9IDAsIGNvbD0iYmx1ZSIsIGx0eT0xKQphYmxpbmUocmVzYmYxLCBjb2w9InJlZCIsIGx0eT0zKQphYmxpbmUoaCA9IDMsIGNvbD0iZ3JlZW4iLCBsdHk9MykKYWJsaW5lKGg9LTMsIGNvbD0iZ3JlZW4iLCBsdHk9MykKbGVnZW5kKCJib3R0b21sZWZ0IiwgbGVnZW5kPWMoIkJlc3QgZml0IGxpbmUgb2Ygc3RhbmRhcmRpemVkIHJlc2lkdWFscyIsICJIb3Jpem9udGFsIGxpbmUgeSA9IDAuMCIsICJIb3Jpem9udGFsIGxpbmUsIHkgPSArLy0gMyIpLCBmaWxsID0gYygicmVkIiwiYmx1ZSIsImdyZWVuIiksIGNleCA9IDAuNSkKYGBgCgpgYGB7cn0Kc3VtbWFyeSh3b3JrTW9kZWxWYWxlbmNpYUZ1bGwpCmRmPC0gd29ya01vZGVsVmFsZW5jaWFGdWxsICU+JSAKICB0aWR5KCkKI0dldCB0aGUgdmFyaWFibGVzIHdoaWNoIHRoZXJlIHAtdmFsdWUgbGFyZ2VyIHRoYW4gMC4wNSAKZGYlPiUKICBmaWx0ZXIoZGYkcC52YWx1ZT4wLjA1KQp3b3JrTW9kZWxWYWxlbmNpYVB2YWx1ZSA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtWYWxlbmNpYSkKc3VtbWFyeSh3b3JrTW9kZWxWYWxlbmNpYVB2YWx1ZSkKYGBgCgorIHAtdmFsdWUgYXBwcm9hY2gKCmBgYHtyfQphbm92YSh3b3JrTW9kZWxWYWxlbmNpYVB2YWx1ZSwgd29ya01vZGVsVmFsZW5jaWFGdWxsKQpgYGAKCisgQmVzdCBzdWJzZXQgc2VsZWN0aW9uIG1ldGhvZAoKYGBge3IsIGZpZy5oZWlnaHQ9NX0KYmVzdGZpdHMgPC0gcmVnc3Vic2V0cyhlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBzbm93X2R1cmF0aW9uICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrVmFsZW5jaWEsIG5iZXN0ID0gMSkKcGxvdChiZXN0Zml0cywgc2NhbGU9ImFkanIyIikKYGBgCgpgYGB7cn0Kd29ya01vZGVsVmFsZW5jaWFCZXN0Zml0PC1sbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IHdvcmtWYWxlbmNpYSkKc3VtbWFyeSh3b3JrTW9kZWxWYWxlbmNpYUJlc3RmaXQpCmFub3ZhKHdvcmtNb2RlbFZhbGVuY2lhQmVzdGZpdCwgd29ya01vZGVsVmFsZW5jaWFQdmFsdWUpCmBgYAoKKyBBSUMgd2l0aCBmb3J3YXJkIHNlbGVjdGlvbiBhcHByb2FjaCB0ZXN0IG1ldGhvZAoKYGBge3J9CndvcmtOdWxsTW9kZWw8LSBsbShlbmVyZ3lfZGVtYW5kIH4gMSwgZGF0YSA9IHdvcmtWYWxlbmNpYSkKc3RlcC53b3JraW5nIDwtIHN0ZXBBSUMod29ya051bGxNb2RlbCwgc2NvcGUgPSBsaXN0KGxvd2VyID0gd29ya051bGxNb2RlbCwKdXBwZXIgPSB3b3JrTW9kZWxWYWxlbmNpYUZ1bGwpLCBkaXJlY3Rpb24gPSAiZm9yd2FyZCIsIHRyYWNlPUZBTFNFKQpzdW1tYXJ5KHN0ZXAud29ya2luZykKYGBgCgorIENyb3NzIFZhbGlkYXRpb24gdGVzdCBtZXRob2QKCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkgaW4gQ1YKc2V0LnNlZWQoMTIzKQojIFNldCB1cCByZXBlYXRlZCBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgd2l0aCBrPTEwCnRyYWluLmNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKQojIFRyYWluIHRoZSBtb2RlbApzdGVwLmN2LndvcmsgPC0gdHJhaW4oZW5lcmd5X2RlbWFuZCB+IEVfMSArIEVfMjUgKyB0ZW1wICsgaHVtaWRpdHkgKyBwcmVzc3VyZSArIHdpbmRfc3BlZWQgKyByYWluX2R1cmF0aW9uICsgc25vd19kdXJhdGlvbiArIGZhY3RvcihkYXlfbmlnaHQpICsgZmFjdG9yKHRpbWVfYmFuZCkgKyBmYWN0b3Ioc2Vhc29uKSArIGZhY3Rvcih3ZWF0aGVyX21haW4pLCBkYXRhID0gd29ya1ZhbGVuY2lhLCBtZXRob2QgPSAibGVhcEJhY2t3YXJkIiwKdHVuZUdyaWQgPSBkYXRhLmZyYW1lKG52bWF4ID0gMToxMiksCnRyQ29udHJvbCA9IHRyYWluLmNvbnRyb2wpCnN0ZXAuY3Yud29yayRyZXN1bHRzCnN1bW1hcnkoc3RlcC5jdi53b3JrJGZpbmFsTW9kZWwpCmBgYAoKCmBgYHtyfQp3b3JrTW9kZWxWYWxlbmNpYUNyb3NzIDwtIGxtKGVuZXJneV9kZW1hbmQgfiBFXzEgKyBFXzI1ICsgdGVtcCArIGh1bWlkaXR5ICsgZmFjdG9yKGRheV9uaWdodCkgKyBmYWN0b3IodGltZV9iYW5kKSArIGZhY3RvcihzZWFzb24pICsgZmFjdG9yKHdlYXRoZXJfbWFpbiksIGRhdGEgPSB3b3JrVmFsZW5jaWEpCmFub3ZhKCB3b3JrTW9kZWxWYWxlbmNpYUNyb3NzLCB3b3JrTW9kZWxWYWxlbmNpYVB2YWx1ZSkKc3VtbWFyeSh3b3JrTW9kZWxWYWxlbmNpYUNyb3NzKQpgYGAKCisgT0xTIG1ldGhvZAoKYGBge3IgZmlnLmhlaWdodD00fQojTW9kZWxPTFMgPC0gb2xzX3N0ZXBfYWxsX3Bvc3NpYmxlKHdvcmtNb2RlbFZhbGVuY2lhRnVsbCkKI2NhdCgiXG4iKQojcGxvdCh4ID0gTW9kZWxPTFMkbiwgeSA9IE1vZGVsT0xTJGFkanIpCiNwbG90KHggPSBNb2RlbE9MUyRuLCB5ID0gTW9kZWxPTFMkYWljKQojT0xTc3VtbWFyeSA8LSBNb2RlbE9MUyAlPiUgZ3JvdXBfYnkobikgJT4lICBmaWx0ZXIoYWRqciA9PSBtYXgoYWRqcikpCmBgYAoKU2FtZSByZWR1Y2VkIG1vZGVsIGFzIFAtVmFsdWUgYXBwcm9hY2ggKHRodXMgY29tbWVudGluZyBvdXQgZ2l2ZW4gcHJvY2Vzc2luZyB0aW1lKQoKIyMgU2VsZWN0IHRoZSBmaW5hbCBtb2RlbAoKTW9kZWwgYWRqdXN0ZWQgUi1zcXVhcmUgZnJvbSBhYm92ZSBtZXRob2RzCgpBKSBGdWxsOiAwLjkxNzcKCkIpIFAtdmFsdWU6IDAuOTE3NwoKQykgQmVzdGZpdHM6IDAuOTE3NAoKRCkgQUlDIE1ldGhvZDogMC45MTc3CgpFKSBDcm9zcy12YWxpZGF0aW9uOiAwLjkxNzYKCkZpbmFsIG1vZGVsIGlzIHRoZSB3b3JrTW9kZWxWYWxlbmNpYVB2YWx1ZSB3aXRoIGhpZ2hlc3QgQWRqdXN0ZWQgUi1zcXVhcmUuCgpgYGB7cn0KTWV0aG9kIDwtIGMoJ0Z1bGwnLCdQLXZhbHVlJywgJ0Jlc3RGaXRzJywgJ0FJQyBGb3J3YXJkJywgJ0NvcnNzIFZhbGlkYXRpb24nKQpWYXJpYWJsZXMgPC0gYygxMywxMiw4LDEyLDkpCkNvZWZmaWNpZW50cyA8LSBjKDIzLDIyLDE4LDIyLDE5KQpBZGp1c3RlZF9SIDwtIGMoMC45MTc2OTc0LDAuOTE3Njk3NywwLjkxNzM1MTQsMC45MTc2OTc3LDAuOTE3NTkyMikKVmFsZW5jaWFUYWJsZTwtZGF0YS5mcmFtZShNZXRob2QsVmFyaWFibGVzLENvZWZmaWNpZW50cyxBZGp1c3RlZF9SKQpgYGAKCiMjIyBCdWlsZCB0aGUgRmluYWwgTW9kZWwKCmBgYHtyfQp3b3JrRmluYWwgPC0gd29ya01vZGVsVmFsZW5jaWFQdmFsdWUKYGBgCgoKIyMjIENoZWNrIHRoZSBsaW5lYXJpdHksIGhldGVyb3NjZWRhc3RpY2l0eSwgb3V0bGllciBhbmQgbm9ybWFsIGFzc3VtcHRpb24KCmBgYHtyLCBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdz1jKDIsMikpCiNSZXNpZHVhbCBQbG90CnBsb3QoeD1maXR0ZWQod29ya0ZpbmFsKSwgeT1zdHVkcmVzKHdvcmtGaW5hbCksIHhsYWIgPSAiRml0dGVkIFZhbHVlcyIsCnlsYWIgPSAiU3R1ZGVudGl6ZWQgUmVzaWR1YWxzIiwgbWFpbj0nUmVzaWR1YWxzIFBsb3QnLCBjb2wgPSBpZmVsc2Uoc3R1ZHJlcyh3b3JrRmluYWwpIDwgLTMsInJlZCIsaWZlbHNlKHN0dWRyZXMod29ya0ZpbmFsKSA+IDMsInJlZCIsImJsYWNrIikpKQphYmxpbmUoaD0tMywgY29sPSJyZWQiLCBsdHk9MikKYWJsaW5lKGg9MywgY29sPSJyZWQiLCBsdHk9MikKI25vcm0gdGVzdApzdGQucmVzIDwtIHJzdGFuZGFyZCh3b3JrRmluYWwpCmFkLnRlc3Qoc3RkLnJlcykKcXFub3JtKHN0dWRyZXMod29ya0ZpbmFsKSwgcGNoID0gMSwgZnJhbWUgPSBGQUxTRSwgbWFpbj1leHByZXNzaW9uKCJBRCB0ZXN0ID0yLjIqMTAiXi0xNikpCnFxbGluZShzdHVkcmVzKHdvcmtGaW5hbCksIGNvbCA9ICJzdGVlbGJsdWUiLCBsd2QgPSAyKQojbGV2ZXJhZ2UgCmxldjwtaGF0dmFsdWVzKHdvcmtGaW5hbCkKY3V0bGV2ID0gKDIqbGVuZ3RoKGNvZWZmaWNpZW50cyh3b3JrRmluYWwpKSkvbnJvdyh3b3JrVmFsZW5jaWEpCiMgQ291bnQgYW5kIGFzc2VzcyB0aGUgbnVtYmVyIG9mIGxldmVyYWdlIHZhbHVlcyA+IGN1dC1vZmYKcG90b3V0bGllciA8LSBzdW0obGV2ID4gY3V0bGV2LCBuYS5ybT1UUlVFKQp0b3Rjb3VudCA9IGxlbmd0aChmaXR0ZWQod29ya0ZpbmFsKSkKcHJpbnQocGFzdGUoIlRoZSBudW1iZXIgb2YgbGV2ZXJhZ2UgcG9pbnRzIHRoYXQgYXJlIHBvdGVudGlhbCBvdXRsaWVycyBpcyIsIHBvdG91dGxpZXIsIi4gIFRoaXMgaXMiLCByb3VuZCgxMDAqcG90b3V0bGllci90b3Rjb3VudCwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHByZWRpY3RlZCB2YWx1ZXMsICgiLCB0b3Rjb3VudCwiKSB0aHVzIG5vdCBtYXRlcmlhbC4iKSkKI2JhcnBsb3QobGV2LCB5bGltID0gYygwLCAyKmN1dGxldikpCnBsb3QobGV2LCB0eXBlPSJoIiwgeWxpbSA9IGMoMCwgMipjdXRsZXYpLCB5bGFiPSJMZXZlcmFnZSBmb3IgdGhlIHdvcmtpbmcgc2V0IikKYWJsaW5lKGggPSBjdXRsZXYsIGNvbCA9ICJyZWQiLCBsdHk9MikKI2Nvb2sgZGlzdGFuY2UKY29va2Rpc3Q8LWNvb2tzLmRpc3RhbmNlKHdvcmtGaW5hbCkKI2JhcnBsb3QoY29va2Rpc3QsIHlsaW09YygwLDEuMDEpLCBtYWluID0gIkNvb2sncyBEaXN0YW5jZSBwbG90IikKI2FibGluZShoID0gMSwgY29sID0gInJlZCIpCmdncGxvdChhcy5kYXRhLmZyYW1lKGNvb2tkaXN0KSxhZXMoeD0xOm5yb3coYXMuZGF0YS5mcmFtZShjb29rZGlzdCkpLHk9Y29va2Rpc3QpKSArCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMSxjb2xvciA9ICJyZWQiKSkrCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxLjEpKSsKICBzY2FsZV95X2JyZWFrKGJyZWFrcyA9IGMoMC4wNSwwLjkpLCBzY2FsZXMgPTAuMSkgKwogIGd1aWRlcyhjb2xvciA9ICJub25lIikgKwogIGxhYnModGl0bGU9IkNvb2sncyBEaXN0YW5jZSIsCiAgICAgICAgeCA9IkluZGV4IiwgeSA9ICIiKQpgYGAKCgpgYGB7cn0KIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoIlZhbGVuY2lhQXNzdW1wdGlvbkNoZWNrMS5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDcsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbAogICAKIyBwYXIobWZyb3c9YygyLDIpKQojcmVzaWR1YWxzIHNjYXR0ZXIgcGxvdApwbG90KHg9Zml0dGVkKHdvcmtGaW5hbCksIHk9c3R1ZHJlcyh3b3JrRmluYWwpLCB4bGFiID0gIkZpdHRlZCBWYWx1ZXMiLAp5bGFiID0gIlN0dWRlbnRpemVkIFJlc2lkdWFscyIsIG1haW49J1Jlc2lkdWFscyBQbG90JywgY29sID0gaWZlbHNlKHN0dWRyZXMod29ya0ZpbmFsKSA8IC0zLCJyZWQiLGlmZWxzZShzdHVkcmVzKHdvcmtGaW5hbCkgPiAzLCJyZWQiLCJibGFjayIpKSkKYWJsaW5lKGg9LTMsIGNvbD0icmVkIiwgbHR5PTIpCmFibGluZShoPTMsIGNvbD0icmVkIiwgbHR5PTIpCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKCiNub3JtYWxpdHkgcGxvdApwZGYoIlZhbGVuY2lhQXNzdW1wdGlvbkNoZWNrMi5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDcsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbApxcW5vcm0oc3R1ZHJlcyh3b3JrRmluYWwpLCBwY2ggPSAxLCBmcmFtZSA9IEZBTFNFLCBtYWluPWV4cHJlc3Npb24oIkFEIHRlc3QgPTIuMioxMCJeLTE2KSkKcXFsaW5lKHN0dWRyZXMod29ya0ZpbmFsKSwgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDIpCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKCiNsZXZlcmFnZSBwbG90CnBkZigiVmFsZW5jaWFBc3N1bXB0aW9uQ2hlY2szLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCgpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIEJhcmNlbG9uYSB3b3JraW5nIHNldCIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKCiNDb29rJ3MgZGlzdGFuY2UgcGxvdApwZGYoIlZhbGVuY2lhQXNzdW1wdGlvbkNoZWNrNC5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDcsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbApnZ3Bsb3QoYXMuZGF0YS5mcmFtZShjb29rZGlzdCksYWVzKHg9MTpucm93KGFzLmRhdGEuZnJhbWUoY29va2Rpc3QpKSx5PWNvb2tkaXN0KSkgKwogIGdlb21fbGluZSgpICsgCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDEsY29sb3IgPSAicmVkIikpKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMS4xKSkrCiAgc2NhbGVfeV9icmVhayhicmVha3MgPSBjKDAuMDA1LDAuOSksIHNjYWxlcyA9MC4xKSArCiAgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSArCiAgbGFicyh0aXRsZT0iQ29vaydzIERpc3RhbmNlIiwKICAgICAgICB4ID0iSW5kZXgiLCB5ID0gIiIpCmRldi5vZmYoKQoKYGBgCgojIyMgVXNlIHRoZSBlc3RpbWF0ZWQgbW9kZWwgdG8gcHJlZGljdCB0aGUgdmFsdWVzIGluIHRoZSBldmFsdWF0aW9uIHNldAoKYGBge3J9Cm5ld2RhdGEgPC0gZXZhbFZhbGVuY2lhWyBjKC0yLC0xNCldCnByZWRpY3QuZXZhbCA8LSBwcmVkaWN0KHdvcmtGaW5hbCwgbmV3ZGF0YSkKcGxvdCh4ID0gZXZhbFZhbGVuY2lhJGVuZXJneV9kZW1hbmQsIHk9cHJlZGljdC5ldmFsLCB4bGFiPSJhY3R1YWwgZW5lcmd5IGRlbWFuZCBmb3IgdGhlIGV2YWx1YXRpb24gc2V0IiwKeWxhYj0icHJlZGljdGVkIGVuZXJneSBkZW1hbmQgZm9yIHRoZSBldmFsdWF0aW9uIHNldCIsIG1haW4gPSAiVmFsZW5jaWEiKQphYmxpbmUoYT0wLCBiPTEsIGNvbD0iYmx1ZSIpCmdyaWQoMTAsMTApCmBgYAoKUHJlcGFyZSByZXBvcnQgZmlndXJlCmBgYHtyIGZpZy5oZWlnaHQ9NH0KIyBPcGVuaW5nIHRoZSBncmFwaGljYWwgZGV2aWNlCiMgQ3VzdG9taXppbmcgdGhlIG91dHB1dApwZGYoIlZhbGVuY2lhRXZhbEFjdENvbXAucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKICAgCnBhcihtZnJvdz1jKDEsMSkpCnBsb3QoeCA9IGV2YWxWYWxlbmNpYSRlbmVyZ3lfZGVtYW5kLCB5PXByZWRpY3QuZXZhbCwgeGxhYj0iQWN0dWFsIGVuZXJneSBkZW1hbmQgZm9yIHRoZSBldmFsdWF0aW9uIHNldCIsCnlsYWI9IlByZWRpY3RlZCBlbmVyZ3kgZGVtYW5kIGZvciB0aGUgZXZhbHVhdGlvbiBzZXQiLCBtYWluID0gIlZhbGVuY2lhIiwgY2V4LmxhYiA9IDAuNzUpCmFibGluZShhPTAsIGI9MSwgY29sPSJibHVlIikKZ3JpZCgxMCwxMCkKCiMgQ2xvc2luZyB0aGUgZ3JhcGhpY2FsIGRldmljZQpkZXYub2ZmKCkKYGBgCgojIyMgQ29tcHV0ZSB0aGUgUk1TRSBmb3IgdGhlIHByZWRpY3Rpb25zCgpgYGB7cn0Kcm9vdG1zZSA8LSBybXNlKGV2YWxWYWxlbmNpYSRlbmVyZ3lfZGVtYW5kLCBwcmVkaWN0LmV2YWwpCnJvb3Rtc2UgPC0gcm1zZShldmFsZGF0YSRlbmVyZ3lfZGVtYW5kLCBwcmVkaWN0LmV2YWwpCnByaW50KHBhc3RlKCJUaGUgcm9vdCBtZWFuIHNxdWFyZSBlcnJvciBvZiB0aGUgcHJlZGljdGVkIHZzIGFjdHVhbCIsbnJvdyhldmFsZGF0YSksImhvdXJseSBlbmVyZ3lfZGF0YSBwb2ludHMgZm9yIFZhbGVuY2lhIG92ZXIgdGhlIDIwMTUtMjAxOCB0aW1lc3BhbiBpcyBhbiBlbmVyZ3kgZGVtYW5kIGRpZmZlcmVuY2Ugb2YiLHJvdW5kKHJvb3Rtc2UsMiksIk1XaC4iKSkKY2F0KCJcbiIpCmRlbWFuZGNvbXBkZiA8LSBkYXRhLmZyYW1lKCJBY3R1YWwgKE1XaCkiID0gZXZhbFZhbGVuY2lhJGVuZXJneV9kZW1hbmQsIlByZWRpY3RlZCAoTVdoKSIgPSBwcmVkaWN0LmV2YWwpCmhlYWQoZGVtYW5kY29tcGRmLDRMKQpjYXQoIlxuIikKcHJpbnQocGFzdGUoIkZvciBleGFtcGxlLCB0aGUgZmlyc3QgYWN0dWFsIGRlbWFuZCB2YWx1ZSBpcyIscm91bmQoZXZhbGRhdGEkZW5lcmd5X2RlbWFuZFsxXSwyKSwiTVdoIGFuZCB0aGUgY29ycmVzcG9uZGluZyBwcmVkaWN0ZWQgdmFsdWUgaXMiLHJvdW5kKHByZWRpY3QuZXZhbFsxXSwyKSwiTVdoLiBUaGUgcGVyY2VudCBlcnJvciBiZXR3ZWVuIHRoaXMgcHJlZGljdGVkIHZhbHVlIHJlbGF0aXZlIHRvIHRoZSBhY3R1YWwgdmFsdWUgaXMiLHJvdW5kKCgxMDAqKHByZWRpY3QuZXZhbFsxXSAtIGV2YWxkYXRhJGVuZXJneV9kZW1hbmRbMV0pL2V2YWxkYXRhJGVuZXJneV9kZW1hbmRbMV0pLDIpLCIlLiIpKQpjYXQoIlxuIikKZGlmZnBjdCA8LSBjKChhYnMoZXZhbGRhdGEkZW5lcmd5X2RlbWFuZCAtIHByZWRpY3QuZXZhbCkpL2V2YWxkYXRhJGVuZXJneV9kZW1hbmQpCnByaW50KHBhc3RlKCJUaGUgbWVhbiBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIixucm93KGV2YWxkYXRhKSwiZXZhbHVhdGlvbiBkYXRhIHNldCBhY3R1YWwgYW5kIG1vZGVsIHByZWRpY3RlZCB2YWx1ZXMgaXMiLHJvdW5kKDEwMCptZWFuKGRpZmZwY3QpLDIpLCIlLiIpKQpgYGAKCiMjIENvbXB1dGUgYW5kIGFuYWx5emUgSGF0IE1hdHJpeCBmb3IgRXZhbHVhdGlvbiBkYXRhIHNldApPYnRhaW4gWCBtYXRyaXggZnJvbSB0aGUgbW9kZWwKYGBge3J9ClByZWRpY3RvcnMgPC1yb3duYW1lcyhzdW1tYXJ5KHdvcmtGaW5hbCkkY29lZmZpY2llbnRzWyxdKQp3b3JrcHJlZGljdG9ycyA8LSBhcy5kYXRhLmZyYW1lKFByZWRpY3RvcnMpClggPC0gbW9kZWwubWF0cml4KHdvcmtGaW5hbCkKY2xhc3MoWCkKY2F0KCJcbiIpCmRpbShYKQpjYXQoIlxuIikKaGVhZChYLCA0TCkKYGBgCgpDcmVhdGUgeF9uZXcgbWF0cml4IHVzaW5nIHRoZSB2YWxpZGF0aW9uIGRhdGEuICBOb3RlIHRoYXQgdGhlIHB1cnBvc2Ugb2YgcnVubmluZyB0aGUgZXZhbHVhdGlvbiByZWdyZXNzaW9uIGhlcmUgaXMgdG8gZXh0cmFjdCBhbGwgb2YgdGhlIGNvcnJlY3QgcHJlZGljdG9yIGNvZWZmaWNpZW50cy4KYGBge3J9CmV2YWxGaW5hbCA8LSBsbShlbmVyZ3lfZGVtYW5kIH4gRV8xICsgRV8yNSArIHRlbXAgKyBodW1pZGl0eSArIHByZXNzdXJlICsgd2luZF9zcGVlZCArIHJhaW5fZHVyYXRpb24gKyBmYWN0b3IoZGF5X25pZ2h0KSArIGZhY3Rvcih0aW1lX2JhbmQpICsgZmFjdG9yKHNlYXNvbikgKyBmYWN0b3Iod2VhdGhlcl9tYWluKSwgZGF0YSA9IGV2YWxWYWxlbmNpYSkKUHJlZGljdG9ycyA8LXJvd25hbWVzKHN1bW1hcnkoZXZhbEZpbmFsKSRjb2VmZmljaWVudHNbLF0pCmV2YWxwcmVkaWN0b3JzIDwtIGFzLmRhdGEuZnJhbWUoUHJlZGljdG9ycykKeF9uZXcxIDwtIG1vZGVsLm1hdHJpeChldmFsRmluYWwpCmNhdCgiXG4iKQpkaW0oeF9uZXcxKQpjYXQoIlxuIikKI0NoZWNrIGlmIHRoZXJlJ3MgYSBwcmVkaWN0b3IgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB3b3JrIGFuZCBldmFsIG1hdHJpY2VzCndvcmtldmFsY29tcGFyZSA8LWFudGlfam9pbih3b3JrcHJlZGljdG9ycyxldmFscHJlZGljdG9ycykKY2F0KCJcbiIpCiNJZiB3b3JrZXZhbGNvbXBhcmUgaXMgJ05vIGRhdGEgYXZhaWxhYmxlIGluIHRhYmxlJywgcnVuIHRoZSBuZXh0IGxpbmUgb2YgY29kZSBhbmQgc2tpcCB0byBsaW5lIDE5NjAKeF9uZXcgPC0geF9uZXcxCiNPdGhlcndpc2Ugd2hlbiB3b3JrZXZhbGNvbXBhcmUgaXMgbm90ICdObyBkYXRhIGF2YWlsYWJsZSBpbiB0YWJsZScsIHJ1biB0aGUgZm9sbG93aW5nIGxpbmVzIG9mIGNvZGUgYWZ0ZXIgaWRlbnRpZnlpbmcgd2hpY2ggY29sdW1uIGlzIG1pc3NpbmcgYmV0d2VlbiBYIGFuZCB4X25ldzEgKG5vIGRpc2NyZXBhbmN5IHdhcyBmb3VuZCBiZXR3ZWVuIHdvcmtWYWxlbmNpYSBidXQgbm90IGluIGV2YWxWYWxlbmNpYSkuCiN4X25ld18gPC1hcy5kYXRhLmZyYW1lKGNiaW5kKHhfbmV3MSxyZXAoMCxucm93KHhfbmV3MSkpKSkKI2R1bW15X3huZXc8LSB4X25ld18gJT4lIHJlbG9jYXRlKFYyMSwuYmVmb3JlID0gMTYpICU+JSByZW5hbWUoImZhY3Rvcih3ZWF0aGVyX21haW4pNCIgPSBWMjEpCiN4X25ldyA8LSBhcy5tYXRyaXgoZHVtbXlfeG5ldykgIApjYXQoIlxuIikKY2xhc3MoeF9uZXcpCmNhdCgiXG4iKQpkaW0oeF9uZXcpCmNhdCgiXG4iKQpoZWFkKHhfbmV3LCA0TCkKYGBgCgpDb21wdXRlIHRoZSBoYXQgdmFsdWVzIGZvciB0aGUgdmFsaWRhdGlvbiBkYXRhCmBgYHtyfQpoX25ld19taWQgPC0gc29sdmUodChYKSUqJVgpCmRpbShoX25ld19taWQpCmhfbmV3IDwtIHhfbmV3JSolaF9uZXdfbWlkJSoldCh4X25ldykKZGltKGhfbmV3KQpgYGAKClBsb3QgdGhlIGhhdHZhbHVlcyBmb3IgdGhlIGRhdGEgaW4gdGhlIGV2YWx1YXRpb24gc2V0CmBgYHtyIGZpZy5oZWlnaHQ9NH0KI2N1dGxldjIgPSAoMipsZW5ndGgoY29lZmZpY2llbnRzKGV2YWxGaW5hbCkpKS9ucm93KGV2YWxWYWxlbmNpYSkKIyBDb3VudCBhbmQgYXNzZXNzIHRoZSBudW1iZXIgb2YgbGV2ZXJhZ2UgdmFsdWVzID4gY3V0LW9mZgpwb3RvdXRsaWVyMiA8LSBzdW0oZGlhZyhoX25ldykgPiBjdXRsZXYsIG5hLnJtPVRSVUUpCnRvdGNvdW50MiA9IG5yb3coZXZhbFZhbGVuY2lhKQpwbG90KGRpYWcoaF9uZXcpLCB0eXBlID0gImgiLCB5bGFiID0gIkxldmVyYWdlIGZvciB0aGUgdmFsaWRhdGlvbiBkYXRhIHNldCIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCBtYWluID0gIlZhbGVuY2lhIikKYWJsaW5lKGg9Y3V0bGV2LCBjb2w9InJlZCIsIGx0eT0yKQpwcmludChwYXN0ZSgiVGhlIG51bWJlciBvZiBsZXZlcmFnZSBwb2ludHMgdGhhdCBhcmUgcG90ZW50aWFsIG91dGxpZXJzIGlzIiwgcG90b3V0bGllcjIsIi4gIFRoaXMgaXMiLCByb3VuZCgxMDAqcG90b3V0bGllcjIvdG90Y291bnQyLDIpLCIlIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdGVkIHZhbHVlcywgKCIsIHRvdGNvdW50MiwiKSB0aHVzIG5vdCBtYXRlcmlhbC4iKSkKYGBgCgpTaWRlLWJ5LXNpZGUgY29tcGFyaXNpb24KYGBge3IgZmlnLmhlaWdodD00fQpwYXIobWZyb3c9YygxLDIpKQpwbG90KGxldiwgdHlwZT0iaCIsIHlsaW0gPSBjKDAsIDIqY3V0bGV2KSwgeWxhYj0iTGV2ZXJhZ2UgZm9yIHRoZSB3b3JraW5nIGRhdGEgc2V0IiwgbWFpbiA9ICJWYWxlbmNpYSIpCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCnBsb3QoZGlhZyhoX25ldyksIHR5cGUgPSAiaCIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCB5bGFiID0gIkxldmVyYWdlIGZvciB0aGUgZXZhbHVhdGlvbiBkYXRhIHNldCIsIG1haW4gPSAiVmFsZW5jaWEiKQphYmxpbmUoaD1jdXRsZXYsIGNvbD0icmVkIiwgbHR5PTIpCnByaW50KHBhc3RlKCJUaGUgbGV2ZXJhZ2UgY3V0b2ZmIGV4Y2VlZGVuY2VzIGluIHRoZSBldmFsdWF0aW9uIHNldCBpcyIscm91bmQoMTAwKnBvdG91dGxpZXIyL3RvdGNvdW50MiwyKSwiJSBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIG9ic2VydmVkIHZhbHVlcyAoIiwgdG90Y291bnQyLCIpIGFuZCBpcyBjb21wYXJhYmxlIHRvIHRoYXQgb2YgdGhlIHdvcmtpbmcgc2V0LCIsIHJvdW5kKDEwMCpwb3RvdXRsaWVyL3RvdGNvdW50LDIpLCIlIG9mIHRoZSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdGVkIHZhbHVlcyAoIiwgdG90Y291bnQsIikuIikpCmBgYAoKUHJlcGFyZSByZXBvcnQgZmlndXJlCmBgYHtyfQojIE9wZW5pbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKIyBDdXN0b21pemluZyB0aGUgb3V0cHV0CnBkZigiVmFsZW5jaWFMZXZDb21wLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gMTQsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbAogICAKcGFyKG1mcm93PWMoMSwyKSkKcGxvdChsZXYsIHR5cGU9ImgiLCB5bGFiID0gIkxldmVyYWdlIiwgeWxpbSA9IGMoMCwyKmN1dGxldiksIHlheHQgPSAnbicsIG1haW4gPSAiVmFsZW5jaWEgV29ya2luZyBEYXRhIiwgcmVzID02MDApCmFibGluZShoID0gY3V0bGV2LCBjb2wgPSAicmVkIiwgbHR5PTIpCmF4aXMoc2lkZSA9IDIsIGF0PXNlcSgwLDIqcm91bmQoY3V0bGV2LDUpLGJ5PXJvdW5kKGN1dGxldiw1KSkpCgpwbG90KGRpYWcoaF9uZXcpLCB0eXBlID0gImgiLCB5bGFiID0gIiIsIHlsaW0gPSBjKDAsMipjdXRsZXYpLCB5YXh0ID0gJ24nLCBtYWluID0gIlZhbGVuY2lhIEV2YWx1YXRpb24gRGF0YSIpCmFibGluZShoPWN1dGxldiwgY29sPSJyZWQiLCBsdHk9MikKYXhpcyhzaWRlID0gMiwgYXQ9c2VxKDAsMipyb3VuZChjdXRsZXYsNSksYnk9cm91bmQoY3V0bGV2LDUpKSkKCiMgQ2xvc2luZyB0aGUgZ3JhcGhpY2FsIGRldmljZQpkZXYub2ZmKCkKYGBgCgojIFRyZWUgQmFzZWQgTW9kZWxsaW5nIAoKIyMgQ29kZSBmb3IgQmFyY2Vsb25hCgojIyMgb3JnYW5pemUgZGF0YQoKCmBgYHtyfQojIGltcG9ydCBkYXRhIHJhdyBkYXRhIHNldAp3X0JDTl9yYXcgPC0gcmVhZC5jc3YoImRhdGEvd29ya2luZ19CYXJjZWxvbmEuY3N2IikKZV9CQ05fcmF3IDwtIHJlYWQuY3N2KCJkYXRhL2V2YWx1YXRpb25fQmFyY2Vsb25hLmNzdiIpCndfQkNOIDwtIHdfQkNOX3Jhd1ssLWMoMSwyKV0KZV9CQ04gPC0gZV9CQ05fcmF3WywtYygxLDIpXQpgYGAKCmBgYHtyfQojIGNoYW5nZXMgb2YgZGF0YSB0eXBlCmNsYXNzKHdfQkNOJGRheV9uaWdodCkgPSAiY2F0ZWdvcnkiCmNsYXNzKHdfQkNOJHRpbWVfYmFuZCkgPSAiY2F0ZWdvcnkiCmNsYXNzKHdfQkNOJHNlYXNvbikgPSAiY2F0ZWdvcnkiCmNsYXNzKHdfQkNOJHdlYXRoZXJfbWFpbikgPSAiY2F0ZWdvcnkiCgpjbGFzcyhlX0JDTiRkYXlfbmlnaHQpID0gImNhdGVnb3J5IgpjbGFzcyhlX0JDTiR0aW1lX2JhbmQpID0gImNhdGVnb3J5IgpjbGFzcyhlX0JDTiRzZWFzb24pID0gImNhdGVnb3J5IgpjbGFzcyhlX0JDTiR3ZWF0aGVyX21haW4pID0gImNhdGVnb3J5IgoKIyBjaGVjayBkYXRhIHR5cGVzCnN0cih3X0JDTikKc3RyKGVfQkNOKSAKc3VtbWFyeSh3X0JDTikKCmBgYAoKIyMjIFJGIG1vZGVsaW5nCgpgYGB7cn0KIyBmaXQgcmFuZG9tIGZvcmVzdCBtb2RlbAojIGRlZmF1bHQgUkYgbW9kZWwKc2V0LnNlZWQoMTIzNCkKcmYgPC0gcmFuZG9tRm9yZXN0KGZvcm11bGEgPSBlbmVyZ3lfZGVtYW5kIH4gLiwgZGF0YSA9IHdfQkNOKQoKIyBmaW5kIHRoZSBudW0gb2YgdHJlZXMgaW4gdGhlIGZvcmVzdCB3aXRoIHNtYWxsZXN0IE1TRQpwbG90KHJmKQpncmlkKDEwLDEwKQpzdW1tYXJ5KHJmKQoKCmBgYAoKCgoKYGBge3J9Cgp0ZXN0QkNOIDwtIHJmJG1zZQp0ZXN0QkNOIDwtIGFzLmRhdGEuZnJhbWUodGVzdEJDTikKdGhyZXNob2xkID0gMC4wNSAjIFRocmVzaG9sZCBFcnJvciBNYXJnaW4gKDUgJSkgCgojVmlzdWFsbHksIHRoZSBzbG9wZSBiZWNvbWUgZmxhdCBhcm91bmQgMTAwIGluIHRoZSBwbG90LCBkaXZpZGUgdGVzdCBkYXRhc2V0IGF0IDEwMApJbmZsZWN0aW9uVmFsdWUgPC0gdGVzdEJDTltucm93KHRlc3RCQ04pLF0gKyB0aHJlc2hvbGQqdGVzdEJDTltucm93KHRlc3RCQ04pLF0KCkluZmxlY3Rpb25JbmRleCA8LSBsZW5ndGgodGVzdEJDTlt0ZXN0QkNOPkluZmxlY3Rpb25WYWx1ZV0pICsgMQoKCmBgYAoKCgpgYGB7ciBmaWcuaGVpZ2h0PTR9CiMgbW9kaWZ5IHRoZSBtb2RlbCB3aXRoIGJlc3QgbXRyeSBhbmQgaW1wb3J0YW5jZSBvZiB0aGUgcHJlZGljdG9ycwoKbXRyeSA8LSB0dW5lUkYod19CQ04sd19CQ04kZW5lcmd5X2RlbWFuZCwgbnRyZWVUcnk9SW5mbGVjdGlvbkluZGV4LAogICAgICAgICAgICAgIHN0ZXBGYWN0b3I9MS41LGltcHJvdmU9MC4wMSwgdHJhY2U9VFJVRSwgcGxvdD1UUlVFKQpiZXN0Lm0gPC0gbXRyeVttdHJ5WywgMl0gPT0gbWluKG10cnlbLCAyXSksIDFdCgpyZl9tb2RpZmllZCA8LXJhbmRvbUZvcmVzdChlbmVyZ3lfZGVtYW5kfi4sZGF0YT13X0JDTiwgbXRyeSA9IGJlc3QubSwgaW1wb3J0YW5jZT1UUlVFLG50cmVlPUluZmxlY3Rpb25JbmRleCkKCgoKdmFySW1wUGxvdChyYW5kb21Gb3Jlc3QoZW5lcmd5X2RlbWFuZH4uLGRhdGE9d19CQ04sIG10cnkgPSBiZXN0Lm0sIGltcG9ydGFuY2U9VFJVRSxudHJlZT1JbmZsZWN0aW9uSW5kZXgpLCBtYWluID0gIkltcG9ydGFuY2Ugb2YgdGhlIHZhcmlhYmxlcyIsIG4udmFyID0gMTMpCgpwZGYoIlZhckltcG9ydGFuY2VfQkNOLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gMTQsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbAogICAKCnZhckltcFBsb3QocmFuZG9tRm9yZXN0KGVuZXJneV9kZW1hbmR+LixkYXRhPXdfQkNOLCBtdHJ5ID0gYmVzdC5tLCBpbXBvcnRhbmNlPVRSVUUsbnRyZWU9SW5mbGVjdGlvbkluZGV4KSwgbWFpbiA9ICJJbXBvcnRhbmNlIG9mIHRoZSB2YXJpYWJsZXMiLCBuLnZhciA9IDEzKQojIENsb3NpbmcgdGhlIGdyYXBoaWNhbCBkZXZpY2UKZGV2Lm9mZigpCgpgYGAKCiMjIyBjaGVjayBwZXJmb3JtYW5jZQoKYGBge3J9CiMgY2hlY2sgdGhlIHRpbWUKIyBzeXN0ZW0udGltZShyYW5kb21Gb3Jlc3QoZW5lcmd5X2RlbWFuZH4uLGRhdGE9d19CQ04sIG10cnkgPSBiZXN0Lm0sIGltcG9ydGFuY2U9VFJVRSxudHJlZT1JbmZsZWN0aW9uSW5kZXgpKQpgYGAKCgpgYGB7cn0KIyBhc3Nlc3MgdGhlIHRlc3Qgc2V0IHBlcmZvcm1hbmNlIG9mIHJmX21vZGlmaWVkIHdpdGggTWV0cmljcyBsaWJyYXJ5CnByZWR2YWx1ZSA8LSBwcmVkaWN0KHJmX21vZGlmaWVkLCBlX0JDTikKcm1zZShlX0JDTiRlbmVyZ3lfZGVtYW5kLCBwcmVkdmFsdWUpCgpwZGYoIkJDTjEucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKCnBsb3QoZV9CQ04kZW5lcmd5X2RlbWFuZCwgcHJlZHZhbHVlLCB4bGFiID0gIkFjdHVhbCBlbmVyZ3kgZGVtYW5kIGZvciB0aGUgZXZhbHVhdGlvbiBzZXQgIiwgeWxhYiA9ICJQcmVkaWN0ZWQgZW5lcmd5IGRlbWFuZCBmb3IgdGhlIGV2YWx1YXRpb24gc2V0IiwgbWFpbiA9ICJCYXJjZWxvbmEgKFRyZWUpIiwgY2V4LmxhYj0wLjc1KQphYmxpbmUoYT0wLGI9MSxjb2w9ImJsdWUiKQpncmlkKDEwLDEwKQpkZXYub2ZmKCkKYGBgCgpgYGB7cn0KcHJlZC5ldmFsIDwtIHByZWR2YWx1ZQpkaWZmcGN0IDwtIGMoKGFicyhldmFsZGF0YSRlbmVyZ3lfZGVtYW5kIC0gcHJlZGljdC5ldmFsKSkvZXZhbGRhdGEkZW5lcmd5X2RlbWFuZCkKcHJpbnQocGFzdGUoIlRoZSBtZWFuIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUiLG5yb3coZXZhbGRhdGEpLCJldmFsdWF0aW9uIGRhdGEgc2V0IGFjdHVhbCBhbmQgbW9kZWwgcHJlZGljdGVkIHZhbHVlcyBpcyIscm91bmQoMTAwKm1lYW4oZGlmZnBjdCksMiksIiUuIikpCmBgYAoKCiMjIyByYW5nZXIgbW9kZWxpbmcKCmBgYHtyfQojIGNyZWF0ZSBSRiBtb2RlbCB3aXRoIHJhbmdlciBwYWNrYWdlCnJhbmdlcihmb3JtdWxhID0gZW5lcmd5X2RlbWFuZCB+IC4sIGRhdGEgPSB3X0JDTikKCmBgYAoKIyMjIGNoZWNrIHBlcmZvcm1hbmNlCgpgYGB7cn0KIyBjaGVjayBleGVjdXRpb24gdGltZQpzeXN0ZW0udGltZShyZl9yYW5nZXIgPC0gcmFuZ2VyKGZvcm11bGEgPSBlbmVyZ3lfZGVtYW5kIH4gLiwgZGF0YSA9IHdfQkNOKSkKYGBgCgoKYGBge3J9CiMgcGVyZm9ybWFuY2UKcm1zZSgKICBlX0JDTiRlbmVyZ3lfZGVtYW5kLCAKICBwcmVkaWN0KHJmX3JhbmdlciwgZV9CQ04pJHByZWRpY3Rpb25zCiAgKQoKcGRmKCJCQ04yLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCgpwbG90KGVfQkNOJGVuZXJneV9kZW1hbmQsIAogIHByZWRpY3QocmZfcmFuZ2VyLCBlX0JDTikkcHJlZGljdGlvbnMsIHlsYWIgPSAiUHJlZGljdGlvbiIsIHhsYWIgPSAiRW5lcmd5IERlbWFuZCBvZiBUZXN0IFNldCIsIG1haW4gPSAiUmFuZ2VyIGZvciBCYXJjZWxvbmEiKQpncmlkKDEwLDEwKQpkZXYub2ZmKCkKCgoKCgpgYGAKCgoKCiMjIENvZGUgZm9yIEJpbGJhbwoKIyMjIG9yZ2FuaXplIGRhdGEKCgpgYGB7cn0KIyBpbXBvcnQgZGF0YSByYXcgZGF0YSBzZXQKd19CTEJfcmF3IDwtIHJlYWQuY3N2KCJkYXRhL3dvcmtpbmdfQmlsYmFvLmNzdiIpCmVfQkxCX3JhdyA8LSByZWFkLmNzdigiZGF0YS9ldmFsdWF0aW9uX0JpbGJhby5jc3YiKQp3X0JMQiA8LSB3X0JMQl9yYXdbLC1jKDEsMildCmVfQkxCIDwtIGVfQkxCX3Jhd1ssLWMoMSwyKV0KYGBgCgpgYGB7cn0KIyBjaGFuZ2VzIG9mIGRhdGEgdHlwZQpjbGFzcyh3X0JMQiRkYXlfbmlnaHQpID0gImNhdGVnb3J5IgpjbGFzcyh3X0JMQiR0aW1lX2JhbmQpID0gImNhdGVnb3J5IgpjbGFzcyh3X0JMQiRzZWFzb24pID0gImNhdGVnb3J5IgpjbGFzcyh3X0JMQiR3ZWF0aGVyX21haW4pID0gImNhdGVnb3J5IgoKY2xhc3MoZV9CTEIkZGF5X25pZ2h0KSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9CTEIkdGltZV9iYW5kKSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9CTEIkc2Vhc29uKSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9CTEIkd2VhdGhlcl9tYWluKSA9ICJjYXRlZ29yeSIKCiMgY2hlY2sgZGF0YSB0eXBlcwpzdHIod19CTEIpCnN0cihlX0JMQikgCnN1bW1hcnkod19CTEIpCgpgYGAKCiMjIyBSRiBtb2RlbGluZwoKYGBge3J9CiMgZml0IHJhbmRvbSBmb3Jlc3QgbW9kZWwKIyBkZWZhdWx0IFJGIG1vZGVsCnNldC5zZWVkKDEyMzQpCnJmMiA8LSByYW5kb21Gb3Jlc3QoZm9ybXVsYSA9IGVuZXJneV9kZW1hbmQgfiAuLCBkYXRhID0gd19CTEIpCgojIGZpbmQgdGhlIG51bSBvZiB0cmVlcyBpbiB0aGUgZm9yZXN0IHdpdGggc21hbGxlc3QgTVNFCnBsb3QocmYyKQoKc3VtbWFyeShyZjIpCm4yIDwtIHdoaWNoLm1pbihyZjIkbXNlKQoKYGBgCgoKCgoKYGBge3J9Cgp0ZXN0QkxCIDwtIHJmMiRtc2UKdGVzdEJMQiA8LSBhcy5kYXRhLmZyYW1lKHRlc3RCTEIpCgoKdGhyZXNob2xkID0gMC4wNSAjIFRocmVzaG9sZCBFcnJvciBNYXJnaW4gKDUgJSkgCgojVmlzdWFsbHksIHRoZSBzbG9wZSBiZWNvbWUgZmxhdCBhcm91bmQgMTAwIGluIHRoZSBwbG90LCBkaXZpZGUgdGVzdCBkYXRhc2V0IGF0IDEwMApJbmZsZWN0aW9uVmFsdWUgPC0gdGVzdEJMQltucm93KHRlc3RCTEIpLF0gKyB0aHJlc2hvbGQqdGVzdEJMQltucm93KHRlc3RCTEIpLF0KCkluZmxlY3Rpb25JbmRleCA8LSBsZW5ndGgodGVzdEJMQlt0ZXN0QkxCPkluZmxlY3Rpb25WYWx1ZV0pICsgMQpgYGAKCgoKYGBge3IgZmlnLmhlaWdodD00fQojIG1vZGlmeSB0aGUgbW9kZWwgd2l0aCBiZXN0IG10cnkgYW5kIGltcG9ydGFuY2Ugb2YgdGhlIHByZWRpY3RvcnMKCm10cnkgPC0gdHVuZVJGKHdfQkxCLHdfQkxCJGVuZXJneV9kZW1hbmQsIG50cmVlVHJ5PUluZmxlY3Rpb25JbmRleCwKICAgICAgICAgICAgICBzdGVwRmFjdG9yPTEuNSxpbXByb3ZlPTAuMDEsIHRyYWNlPVRSVUUsIHBsb3Q9VFJVRSkKYmVzdC5tIDwtIG10cnlbbXRyeVssIDJdID09IG1pbihtdHJ5WywgMl0pLCAxXQoKcmYyX21vZGlmaWVkIDwtcmFuZG9tRm9yZXN0KGVuZXJneV9kZW1hbmR+LixkYXRhPXdfQkxCLCBtdHJ5ID0gYmVzdC5tLCBpbXBvcnRhbmNlPVRSVUUsbnRyZWU9SW5mbGVjdGlvbkluZGV4KQoKCnZhckltcFBsb3QocmFuZG9tRm9yZXN0KGVuZXJneV9kZW1hbmR+LixkYXRhPXdfQkxCLCBtdHJ5ID0gYmVzdC5tLCBpbXBvcnRhbmNlPVRSVUUsbnRyZWU9SW5mbGVjdGlvbkluZGV4KSwgbWFpbiA9ICJJbXBvcnRhbmNlIG9mIHRoZSB2YXJpYmFsZXMiLCBuLnZhciA9IDEzKQoKYGBgCgojIyMgY2hlY2sgcGVyZm9ybWFuY2UKCmBgYHtyfQojIGNoZWNrIHRoZSB0aW1lCnN5c3RlbS50aW1lKHJhbmRvbUZvcmVzdChmb3JtdWxhID0gZW5lcmd5X2RlbWFuZCB+IC4sIAogICAgICAgICAgICAgICAgICAgZGF0YSA9IHdfQkxCLCBudHJlZSA9IG4yLCBuYS5hY3Rpb249bmEuZXhjbHVkZSkpCmBgYAoKCmBgYHtyfQojIGFzc2VzcyB0aGUgdGVzdCBzZXQgcGVyZm9ybWFuY2Ugb2YgcmZfbW9kaWZpZWQgd2l0aCBNZXRyaWNzIGxpYnJhcnkKcHJlZHZhbHVlIDwtIHByZWRpY3QocmYyX21vZGlmaWVkLCBlX0JMQikKcm1zZShlX0JMQiRlbmVyZ3lfZGVtYW5kLCBwcmVkdmFsdWUpCgpwZGYoIkJMQjEucGRmIiwgICAgICAgICAjIEZpbGUgbmFtZQogICAgd2lkdGggPSA3LCBoZWlnaHQgPSA0LCAjIFdpZHRoIGFuZCBoZWlnaHQgaW4gaW5jaGVzCiAgICBiZyA9ICJ3aGl0ZSIsICAgICAgICAgICMgQmFja2dyb3VuZCBjb2xvcgogICAgY29sb3Jtb2RlbCA9ICJjbXlrIikgICMgQ29sb3IgbW9kZWwKCnBsb3QoZV9CTEIkZW5lcmd5X2RlbWFuZCwgcHJlZHZhbHVlLCB4bGFiID0gIkVuZXJneSBEZW1hbmQgb2YgVGVzdCBTZXQgIiwgeWxhYiA9ICJQcmVkaWN0aW9uIiwgbWFpbiA9ICJyRiBmb3IgQmlsYmFvIikKZ3JpZCgxMCwxMCkKZGV2Lm9mZigpCgoKCnBsb3QoZV9CTEIkZW5lcmd5X2RlbWFuZCwgcHJlZHZhbHVlLCB4bGFiID0gIkVuZXJneSBEZW1hbmQgb2YgVGVzdCBTZXQgIiwgeWxhYiA9ICJQcmVkaWN0aW9uIiwgbWFpbiA9ICJyRiBmb3IgQmlsYmFvIikKYGBgCgojIyMgcmFuZ2VyIG1vZGVsaW5nCgpgYGB7cn0KIyBjcmVhdGUgUkYgbW9kZWwgd2l0aCByYW5nZXIgcGFja2FnZQpyZjJfcmFuZ2VyIDwtIHJhbmdlcihmb3JtdWxhID0gZW5lcmd5X2RlbWFuZCB+IC4sIGRhdGEgPSB3X0JMQikKCmBgYAoKIyMjIGNoZWNrIHBlcmZvcm1hbmNlCgpgYGB7cn0KIyBjaGVjayBleGVjdXRpb24gdGltZQojIHN5c3RlbS50aW1lKHJmMl9yYW5nZXIgPC0gcmFuZ2VyKGZvcm11bGEgPSBlbmVyZ3lfZGVtYW5kIH4gLiwgZGF0YSA9IHdfQkxCKSkKYGBgCgoKYGBge3J9CiMgcGVyZm9ybWFuY2UKcm1zZSgKICBlX0JMQiRlbmVyZ3lfZGVtYW5kLCAKICBwcmVkaWN0KHJmMl9yYW5nZXIsIGVfQkxCKSRwcmVkaWN0aW9ucwogICkKCnBkZigiQkxCMi5wZGYiLCAgICAgICAgICMgRmlsZSBuYW1lCiAgICB3aWR0aCA9IDcsIGhlaWdodCA9IDQsICMgV2lkdGggYW5kIGhlaWdodCBpbiBpbmNoZXMKICAgIGJnID0gIndoaXRlIiwgICAgICAgICAgIyBCYWNrZ3JvdW5kIGNvbG9yCiAgICBjb2xvcm1vZGVsID0gImNteWsiKSAgIyBDb2xvciBtb2RlbAoKcGxvdCgKICBlX0JMQiRlbmVyZ3lfZGVtYW5kLCAKICBwcmVkaWN0KHJmMl9yYW5nZXIsIGVfQkxCKSRwcmVkaWN0aW9ucywgeWxhYiA9ICJQcmVkaWN0aW9uIiwgeGxhYiA9ICJFbmVyZ3kgRGVtYW5kIG9mIFRlc3QgU2V0IiwgbWFpbiA9ICJSYW5nZXIgZm9yIEJpbGJhbyIKICApCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKCnBsb3QoCiAgZV9CTEIkZW5lcmd5X2RlbWFuZCwgCiAgcHJlZGljdChyZjJfcmFuZ2VyLCBlX0JMQikkcHJlZGljdGlvbnMsIHlsYWIgPSAiUHJlZGljdGlvbiIsIHhsYWIgPSAiRW5lcmd5IERlbWFuZCBvZiBUZXN0IFNldCIsIG1haW4gPSAiUmFuZ2VyIGZvciBCaWxiYW8iCiAgKQpgYGAKCgoKIyMgQ29kZSBmb3IgTWFkcmlkCgojIyMgb3JnYW5pemUgZGF0YQoKCmBgYHtyfQojIGltcG9ydCBkYXRhIHJhdyBkYXRhIHNldAp3X01ERF9yYXcgPC0gcmVhZC5jc3YoImRhdGEvd29ya2luZ19NYWRyaWQuY3N2IikKZV9NRERfcmF3IDwtIHJlYWQuY3N2KCJkYXRhL2V2YWx1YXRpb25fTWFkcmlkLmNzdiIpCndfTUREIDwtIHdfTUREX3Jhd1ssLWMoMSwyKV0KZV9NREQgPC0gZV9NRERfcmF3WywtYygxLDIpXQpgYGAKCmBgYHtyfQojIGNoYW5nZXMgb2YgZGF0YSB0eXBlCmNsYXNzKHdfTUREJGRheV9uaWdodCkgPSAiY2F0ZWdvcnkiCmNsYXNzKHdfTUREJHRpbWVfYmFuZCkgPSAiY2F0ZWdvcnkiCmNsYXNzKHdfTUREJHNlYXNvbikgPSAiY2F0ZWdvcnkiCmNsYXNzKHdfTUREJHdlYXRoZXJfbWFpbikgPSAiY2F0ZWdvcnkiCgpjbGFzcyhlX01ERCRkYXlfbmlnaHQpID0gImNhdGVnb3J5IgpjbGFzcyhlX01ERCR0aW1lX2JhbmQpID0gImNhdGVnb3J5IgpjbGFzcyhlX01ERCRzZWFzb24pID0gImNhdGVnb3J5IgpjbGFzcyhlX01ERCR3ZWF0aGVyX21haW4pID0gImNhdGVnb3J5IgoKIyBjaGVjayBkYXRhIHR5cGVzCnN0cih3X01ERCkKc3RyKGVfTUREKSAKc3VtbWFyeSh3X01ERCkKCmBgYAoKIyMjIFJGIG1vZGVsaW5nCgpgYGB7cn0KIyBmaXQgcmFuZG9tIGZvcmVzdCBtb2RlbAojIGRlZmF1bHQgUkYgbW9kZWwKc2V0LnNlZWQoMTIzNCkKcmYzIDwtIHJhbmRvbUZvcmVzdChmb3JtdWxhID0gZW5lcmd5X2RlbWFuZCB+IC4sIGRhdGEgPSB3X01ERCkKCiMgZmluZCB0aGUgbnVtIG9mIHRyZWVzIGluIHRoZSBmb3Jlc3Qgd2l0aCBzbWFsbGVzdCBNU0UKcGxvdChyZjMpCgpzdW1tYXJ5KHJmMykKCgpgYGAKCgoKYGBge3J9Cgp0ZXN0TUREIDwtIHJmMyRtc2UKdGVzdE1ERCA8LSBhcy5kYXRhLmZyYW1lKHRlc3RNREQpCgojdGhlIHNsb3BlIGJlY29tZSBmbGF0IGFyb3VuZCAxMDAgaW4gdGhlIHBsb3QsIGRpdmlkZSB0ZXN0IGRhdGFzZXQgYXQgMTAwCgp0aHJlc2hvbGQgPSAwLjA1ICMgVGhyZXNob2xkIEVycm9yIE1hcmdpbiAoNSAlKSAKCiNWaXN1YWxseSwgdGhlIHNsb3BlIGJlY29tZSBmbGF0IGFyb3VuZCAxMDAgaW4gdGhlIHBsb3QsIGRpdmlkZSB0ZXN0IGRhdGFzZXQgYXQgMTAwCkluZmxlY3Rpb25WYWx1ZSA8LSB0ZXN0TUREW25yb3codGVzdE1ERCksXSArIHRocmVzaG9sZCp0ZXN0TUREW25yb3codGVzdE1ERCksXQoKSW5mbGVjdGlvbkluZGV4IDwtIGxlbmd0aCh0ZXN0TUREW3Rlc3RNREQ+SW5mbGVjdGlvblZhbHVlXSkgKyAxCgpgYGAKCgoKYGBge3IgZmlnLmhlaWdodD00fQojIG1vZGlmeSB0aGUgbW9kZWwgd2l0aCBiZXN0IG10cnkgYW5kIGltcG9ydGFuY2Ugb2YgdGhlIHByZWRpY3RvcnMKCm10cnkgPC0gdHVuZVJGKHdfTURELCB3X01ERCRlbmVyZ3lfZGVtYW5kLCBudHJlZVRyeT1JbmZsZWN0aW9uSW5kZXgsCiAgICAgICAgICAgICAgc3RlcEZhY3Rvcj0xLjUsaW1wcm92ZT0wLjAxLCB0cmFjZT1UUlVFLCBwbG90PVRSVUUpCmJlc3QubSA8LSBtdHJ5W210cnlbLCAyXSA9PSBtaW4obXRyeVssIDJdKSwgMV0KCnJmM19tb2RpZmllZCA8LXJhbmRvbUZvcmVzdChlbmVyZ3lfZGVtYW5kfi4sZGF0YT13X01ERCwgbXRyeSA9IGJlc3QubSwgaW1wb3J0YW5jZT1UUlVFLG50cmVlPUluZmxlY3Rpb25JbmRleCkKCgp2YXJJbXBQbG90KHJhbmRvbUZvcmVzdChlbmVyZ3lfZGVtYW5kfi4sZGF0YT13X01ERCwgbXRyeSA9IGJlc3QubSwgaW1wb3J0YW5jZT1UUlVFLG50cmVlPUluZmxlY3Rpb25JbmRleCksIG1haW4gPSAiSW1wb3J0YW5jZSBvZiB0aGUgdmFyaWFibGVzIiwgbi52YXIgPSAxMykKCmBgYAoKIyMjIGNoZWNrIHBlcmZvcm1hbmNlCgpgYGB7cn0KIyBjaGVjayB0aGUgdGltZQojc3lzdGVtLnRpbWUocmFuZG9tRm9yZXN0KGZvcm11bGEgPSBlbmVyZ3lfZGVtYW5kIH4gLiwgCiAgIyAgICAgICAgICAgICAgICAgZGF0YSA9IHdfTURELCBudHJlZSA9IDEwMCwgbmEuYWN0aW9uPW5hLmV4Y2x1ZGUpKQpgYGAKCgpgYGB7cn0KIyBhc3Nlc3MgdGhlIHRlc3Qgc2V0IHBlcmZvcm1hbmNlIG9mIHJmX21vZGlmaWVkIHdpdGggTWV0cmljcyBsaWJyYXJ5CnByZWR2YWx1ZSA8LSBwcmVkaWN0KHJmM19tb2RpZmllZCwgZV9NREQpCnJtc2UoZV9NREQkZW5lcmd5X2RlbWFuZCwgcHJlZHZhbHVlKQoKcGRmKCJNREQxLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCgpwbG90KGVfTUREJGVuZXJneV9kZW1hbmQsIHByZWR2YWx1ZSwgeWxhYiA9ICJQcmVkaWN0aW9uIiwgeGxhYiA9ICJFbmVyZ3kgRGVtYW5kIG9mIFRlc3QgU2V0IiwgbWFpbiA9ICJyRiBmb3IgTWFkcmlkIikKCmdyaWQoMTAsMTApCmRldi5vZmYoKQoKcGxvdChlX01ERCRlbmVyZ3lfZGVtYW5kLCBwcmVkdmFsdWUsIHlsYWIgPSAiUHJlZGljdGlvbiIsIHhsYWIgPSAiRW5lcmd5IERlbWFuZCBvZiBUZXN0IFNldCIsIG1haW4gPSAickYgZm9yIE1hZHJpZCIpCgpgYGAKCiMjIyByYW5nZXIgbW9kZWxpbmcKCmBgYHtyfQojIGNyZWF0ZSBSRiBtb2RlbCB3aXRoIHJhbmdlciBwYWNrYWdlCnJmM19yYW5nZXIgPC0gcmFuZ2VyKGZvcm11bGEgPSBlbmVyZ3lfZGVtYW5kIH4gLiwgZGF0YSA9IHdfTUREKQoKYGBgCgojIyMgY2hlY2sgcGVyZm9ybWFuY2UKCmBgYHtyfQojIGNoZWNrIGV4ZWN1dGlvbiB0aW1lCiMgc3lzdGVtLnRpbWUocmYzX3JhbmdlciA8LSByYW5nZXIoZm9ybXVsYSA9IGVuZXJneV9kZW1hbmQgfiAuLCBkYXRhID0gd19NREQpKQpgYGAKCgpgYGB7cn0KIyBwZXJmb3JtYW5jZQpybXNlKAogIGVfTUREJGVuZXJneV9kZW1hbmQsIAogIHByZWRpY3QocmYzX3JhbmdlciwgZV9NREQpJHByZWRpY3Rpb25zCiAgKQoKcGRmKCJNREQyLnBkZiIsICAgICAgICAgIyBGaWxlIG5hbWUKICAgIHdpZHRoID0gNywgaGVpZ2h0ID0gNCwgIyBXaWR0aCBhbmQgaGVpZ2h0IGluIGluY2hlcwogICAgYmcgPSAid2hpdGUiLCAgICAgICAgICAjIEJhY2tncm91bmQgY29sb3IKICAgIGNvbG9ybW9kZWwgPSAiY215ayIpICAjIENvbG9yIG1vZGVsCgpwbG90KAogIGVfTUREJGVuZXJneV9kZW1hbmQsIAogIHByZWRpY3QocmYyX3JhbmdlciwgZV9NREQpJHByZWRpY3Rpb25zLCB5bGFiID0gIlByZWRpY3Rpb24iLCB4bGFiID0gIkVuZXJneSBEZW1hbmQgb2YgVGVzdCBTZXQiLCBtYWluID0gIlJhbmdlciBmb3IgTWFkcmlkIgogICkKZ3JpZCgxMCwxMCkKZGV2Lm9mZigpCgpwbG90KAogIGVfTUREJGVuZXJneV9kZW1hbmQsIAogIHByZWRpY3QocmYzX3JhbmdlciwgZV9NREQpJHByZWRpY3Rpb25zLCB5bGFiID0gIlByZWRpY3Rpb24iLCB4bGFiID0gIkVuZXJneSBEZW1hbmQgb2YgVGVzdCBTZXQiLCBtYWluID0gIlJhbmdlciBmb3IgTWFkcmlkIgogICkKYGBgCgoKCiMjIENvZGUgZm9yIFNldmlsbGUKCiMjIyBvcmdhbml6ZSBkYXRhCgoKYGBge3J9CiMgaW1wb3J0IGRhdGEgcmF3IGRhdGEgc2V0CndfU1ZMX3JhdyA8LSByZWFkLmNzdigiZGF0YS93b3JraW5nX1NldmlsbGUuY3N2IikKZV9TVkxfcmF3IDwtIHJlYWQuY3N2KCJkYXRhL2V2YWx1YXRpb25fU2V2aWxsZS5jc3YiKQp3X1NWTCA8LSB3X1NWTF9yYXdbLC1jKDEsMildCmVfU1ZMIDwtIGVfU1ZMX3Jhd1ssLWMoMSwyKV0KYGBgCgpgYGB7cn0KIyBjaGFuZ2VzIG9mIGRhdGEgdHlwZQpjbGFzcyh3X1NWTCRkYXlfbmlnaHQpID0gImNhdGVnb3J5IgpjbGFzcyh3X1NWTCR0aW1lX2JhbmQpID0gImNhdGVnb3J5IgpjbGFzcyh3X1NWTCRzZWFzb24pID0gImNhdGVnb3J5IgpjbGFzcyh3X1NWTCR3ZWF0aGVyX21haW4pID0gImNhdGVnb3J5IgoKY2xhc3MoZV9TVkwkZGF5X25pZ2h0KSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9TVkwkdGltZV9iYW5kKSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9TVkwkc2Vhc29uKSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9TVkwkd2VhdGhlcl9tYWluKSA9ICJjYXRlZ29yeSIKCiMgY2hlY2sgZGF0YSB0eXBlcwpzdHIod19TVkwpCnN0cihlX1NWTCkgCnN1bW1hcnkod19TVkwpCgpgYGAKCiMjIyBSRiBtb2RlbGluZwoKYGBge3J9CiMgZml0IHJhbmRvbSBmb3Jlc3QgbW9kZWwKIyBkZWZhdWx0IFJGIG1vZGVsCnNldC5zZWVkKDEyMzQpCnJmNCA8LSByYW5kb21Gb3Jlc3QoZm9ybXVsYSA9IGVuZXJneV9kZW1hbmQgfiAuLCBkYXRhID0gd19TVkwpCiMgZmluZCB0aGUgbnVtIG9mIHRyZWVzIGluIHRoZSBmb3Jlc3Qgd2l0aCBzbWFsbGVzdCBNU0UKcGxvdChyZjQpCgpgYGAKCmBgYHtyfQoKdGVzdFNWTCA8LSByZjQkbXNlCnRlc3RTVkwgPC0gYXMuZGF0YS5mcmFtZSh0ZXN0U1ZMKQoKI3RoZSBzbG9wZSBiZWNvbWUgZmxhdCBhcm91bmQgMTAwIGluIHRoZSBwbG90LCBkaXZpZGUgdGVzdCBkYXRhc2V0IGF0IDEwMAoKdGhyZXNob2xkID0gMC4wNSAjIFRocmVzaG9sZCBFcnJvciBNYXJnaW4gKDUgJSkgCgojVmlzdWFsbHksIHRoZSBzbG9wZSBiZWNvbWUgZmxhdCBhcm91bmQgMTAwIGluIHRoZSBwbG90LCBkaXZpZGUgdGVzdCBkYXRhc2V0IGF0IDEwMApJbmZsZWN0aW9uVmFsdWUgPC0gdGVzdFNWTFtucm93KHRlc3RTVkwpLF0gKyB0aHJlc2hvbGQqdGVzdFNWTFtucm93KHRlc3RTVkwpLF0KCkluZmxlY3Rpb25JbmRleCA8LSBsZW5ndGgodGVzdFNWTFt0ZXN0U1ZMPkluZmxlY3Rpb25WYWx1ZV0pICsgMQoKYGBgCgoKCmBgYHtyIGZpZy5oZWlnaHQ9NH0KIyBtb2RpZnkgdGhlIG1vZGVsIHdpdGggYmVzdCBtdHJ5IGFuZCBpbXBvcnRhbmNlIG9mIHRoZSBwcmVkaWN0b3JzCgptdHJ5IDwtIHR1bmVSRih3X1NWTCwgd19TVkwkZW5lcmd5X2RlbWFuZCwgbnRyZWVUcnk9SW5mbGVjdGlvbkluZGV4LAogICAgICAgICAgICAgIHN0ZXBGYWN0b3I9MS41LGltcHJvdmU9MC4wMSwgdHJhY2U9VFJVRSwgcGxvdD1UUlVFKQpiZXN0Lm0gPC0gbXRyeVttdHJ5WywgMl0gPT0gbWluKG10cnlbLCAyXSksIDFdCgpyZjRfbW9kaWZpZWQgPC1yYW5kb21Gb3Jlc3QoZW5lcmd5X2RlbWFuZH4uLGRhdGE9d19TVkwsIG10cnkgPSBiZXN0Lm0sIGltcG9ydGFuY2U9VFJVRSxudHJlZT1JbmZsZWN0aW9uSW5kZXgpCgoKdmFySW1wUGxvdChyYW5kb21Gb3Jlc3QoZW5lcmd5X2RlbWFuZH4uLGRhdGE9d19TVkwsIG10cnkgPSBiZXN0Lm0sIGltcG9ydGFuY2U9VFJVRSxudHJlZT1JbmZsZWN0aW9uSW5kZXgpLCBtYWluID0gIkltcG9ydGFuY2Ugb2YgdGhlIHZhcmlhYmxlcyIsIG4udmFyID0gMTMpCgpgYGAKCgoKCgojIyMgY2hlY2sgcGVyZm9ybWFuY2UKCmBgYHtyfQojIGNoZWNrIHRoZSB0aW1lCiMgc3lzdGVtLnRpbWUocmFuZG9tRm9yZXN0KGZvcm11bGEgPSBlbmVyZ3lfZGVtYW5kIH4gLiwgCiMgICAgICAgICAgICAgICAgIGRhdGEgPSB3X1NWTCwgbnRyZWUgPSBuNCwgbmEuYWN0aW9uPW5hLmV4Y2x1ZGUpKQpgYGAKCgpgYGB7cn0KIyBhc3Nlc3MgdGhlIHRlc3Qgc2V0IHBlcmZvcm1hbmNlIG9mIHJmX21vZGlmaWVkIHdpdGggTWV0cmljcyBsaWJyYXJ5CnByZWR2YWx1ZSA8LSBwcmVkaWN0KHJmNF9tb2RpZmllZCwgZV9TVkwpCnJtc2UoZV9TVkwkZW5lcmd5X2RlbWFuZCwgcHJlZHZhbHVlKQpwbG90KGVfU1ZMJGVuZXJneV9kZW1hbmQsIHByZWR2YWx1ZSwgbWFpbiA9ICJTZXZpbGxlMSIpCmBgYAoKIyMjIHJhbmdlciBtb2RlbGluZwoKYGBge3J9CiMgY3JlYXRlIFJGIG1vZGVsIHdpdGggcmFuZ2VyIHBhY2thZ2UKcmY0X3JhbmdlciA8LSByYW5nZXIoZm9ybXVsYSA9IGVuZXJneV9kZW1hbmQgfiAuLCBkYXRhID0gd19TVkwpCgpgYGAKCiMjIyBjaGVjayBwZXJmb3JtYW5jZQoKYGBge3J9CiMgY2hlY2sgZXhlY3V0aW9uIHRpbWUKIyBzeXN0ZW0udGltZShyZjRfcmFuZ2VyIDwtIHJhbmdlcihmb3JtdWxhID0gZW5lcmd5X2RlbWFuZCB+IC4sIGRhdGEgPSB3X1NWTCkpCmBgYAoKCgoKCmBgYHtyfQojIHBlcmZvcm1hbmNlCnJtc2UoCiAgZV9TVkwkZW5lcmd5X2RlbWFuZCwgCiAgcHJlZGljdChyZjRfcmFuZ2VyLCBlX1NWTCkkcHJlZGljdGlvbnMKICApCnBsb3QoCiAgZV9TVkwkZW5lcmd5X2RlbWFuZCwgCiAgcHJlZGljdChyZjRfcmFuZ2VyLCBlX1NWTCkkcHJlZGljdGlvbnMsIHhsYWIgPSAicHJlZHZhbHVlIiwgbWFpbiA9ICJTZXZpbGxlMiIKICApCmBgYAoKCiMjIENvZGUgZm9yIFZhbGVuY2lhCgojIyMgb3JnYW5pemUgZGF0YQoKCmBgYHtyfQojIGltcG9ydCBkYXRhIHJhdyBkYXRhIHNldAp3X1ZMQ19yYXcgPC0gcmVhZC5jc3YoImRhdGEvd29ya2luZ19WYWxlbmNpYS5jc3YiKQplX1ZMQ19yYXcgPC0gcmVhZC5jc3YoImRhdGEvZXZhbHVhdGlvbl9WYWxlbmNpYS5jc3YiKQp3X1ZMQyA8LSB3X1ZMQ19yYXdbLC1jKDEsMildCmVfVkxDIDwtIGVfVkxDX3Jhd1ssLWMoMSwyKV0KYGBgCgpgYGB7cn0KIyBjaGFuZ2VzIG9mIGRhdGEgdHlwZQpjbGFzcyh3X1ZMQyRkYXlfbmlnaHQpID0gImNhdGVnb3J5IgpjbGFzcyh3X1ZMQyR0aW1lX2JhbmQpID0gImNhdGVnb3J5IgpjbGFzcyh3X1ZMQyRzZWFzb24pID0gImNhdGVnb3J5IgpjbGFzcyh3X1ZMQyR3ZWF0aGVyX21haW4pID0gImNhdGVnb3J5IgoKY2xhc3MoZV9WTEMkZGF5X25pZ2h0KSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9WTEMkdGltZV9iYW5kKSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9WTEMkc2Vhc29uKSA9ICJjYXRlZ29yeSIKY2xhc3MoZV9WTEMkd2VhdGhlcl9tYWluKSA9ICJjYXRlZ29yeSIKCiMgY2hlY2sgZGF0YSB0eXBlcwpzdHIod19WTEMpCnN0cihlX1ZMQykgCnN1bW1hcnkod19WTEMpCgpgYGAKCiMjIyBSRiBtb2RlbGluZwoKYGBge3J9CiMgZml0IHJhbmRvbSBmb3Jlc3QgbW9kZWwKIyBkZWZhdWx0IFJGIG1vZGVsCnNldC5zZWVkKDEyMzQpCnJmNSA8LSByYW5kb21Gb3Jlc3QoZm9ybXVsYSA9IGVuZXJneV9kZW1hbmQgfiAuLCBkYXRhID0gd19WTEMpCiMgZmluZCB0aGUgbnVtIG9mIHRyZWVzIGluIHRoZSBmb3Jlc3Qgd2l0aCBzbWFsbGVzdCBNU0UKcGxvdChyZjUpCgpgYGAKCmBgYHtyfQoKdGVzdFZMQyA8LSByZjUkbXNlCnRlc3RWTEMgPC0gYXMuZGF0YS5mcmFtZSh0ZXN0VkxDKQoKI3RoZSBzbG9wZSBiZWNvbWUgZmxhdCBhcm91bmQgMTAwIGluIHRoZSBwbG90LCBkaXZpZGUgdGVzdCBkYXRhc2V0IGF0IDEwMAoKdGhyZXNob2xkID0gMC4wNSAjIFRocmVzaG9sZCBFcnJvciBNYXJnaW4gKDUgJSkgCgojVmlzdWFsbHksIHRoZSBzbG9wZSBiZWNvbWUgZmxhdCBhcm91bmQgMTAwIGluIHRoZSBwbG90LCBkaXZpZGUgdGVzdCBkYXRhc2V0IGF0IDEwMApJbmZsZWN0aW9uVmFsdWUgPC0gdGVzdFZMQ1tucm93KHRlc3RWTEMpLF0gKyB0aHJlc2hvbGQqdGVzdFZMQ1tucm93KHRlc3RWTEMpLF0KCkluZmxlY3Rpb25JbmRleCA8LSBsZW5ndGgodGVzdFZMQ1t0ZXN0VkxDPkluZmxlY3Rpb25WYWx1ZV0pICsgMQoKYGBgCgoKCmBgYHtyIGZpZy5oZWlnaHQ9NH0KIyBtb2RpZnkgdGhlIG1vZGVsIHdpdGggYmVzdCBtdHJ5IGFuZCBpbXBvcnRhbmNlIG9mIHRoZSBwcmVkaWN0b3JzCgptdHJ5IDwtIHR1bmVSRih3X1ZMQywgd19WTEMkZW5lcmd5X2RlbWFuZCwgbnRyZWVUcnk9SW5mbGVjdGlvbkluZGV4LAogICAgICAgICAgICAgIHN0ZXBGYWN0b3I9MS41LGltcHJvdmU9MC4wMSwgdHJhY2U9VFJVRSwgcGxvdD1UUlVFKQpiZXN0Lm0gPC0gbXRyeVttdHJ5WywgMl0gPT0gbWluKG10cnlbLCAyXSksIDFdCgpyZjVfbW9kaWZpZWQgPC1yYW5kb21Gb3Jlc3QoZW5lcmd5X2RlbWFuZH4uLGRhdGE9d19WTEMsIG10cnkgPSBiZXN0Lm0sIGltcG9ydGFuY2U9VFJVRSxudHJlZT1JbmZsZWN0aW9uSW5kZXgpCgoKdmFySW1wUGxvdChyYW5kb21Gb3Jlc3QoZW5lcmd5X2RlbWFuZH4uLGRhdGE9d19WTEMsIG10cnkgPSBiZXN0Lm0sIGltcG9ydGFuY2U9VFJVRSxudHJlZT1JbmZsZWN0aW9uSW5kZXgpLCBtYWluID0gIkltcG9ydGFuY2Ugb2YgdGhlIHZhcmlhYmxlcyIsIG4udmFyID0gMTMpCgpgYGAKCgoKCiMjIyBjaGVjayBwZXJmb3JtYW5jZQoKYGBge3J9CiMgY2hlY2sgdGhlIHRpbWUKIyBzeXN0ZW0udGltZShyYW5kb21Gb3Jlc3QoZm9ybXVsYSA9IGVuZXJneV9kZW1hbmQgfiAuLCAKICAgIyAgICAgICAgICAgICAgICAgZGF0YSA9IHdfVkxDLCBudHJlZSA9IG41LCBuYS5hY3Rpb249bmEuZXhjbHVkZSkpCmBgYAoKCmBgYHtyfQojIGFzc2VzcyB0aGUgdGVzdCBzZXQgcGVyZm9ybWFuY2Ugb2YgcmZfbW9kaWZpZWQgd2l0aCBNZXRyaWNzIGxpYnJhcnkKcHJlZHZhbHVlIDwtIHByZWRpY3QocmY1X21vZGlmaWVkLCBlX1ZMQykKcm1zZShlX1ZMQyRlbmVyZ3lfZGVtYW5kLCBwcmVkdmFsdWUpCnBsb3QoZV9WTEMkZW5lcmd5X2RlbWFuZCwgcHJlZHZhbHVlLCBtYWluID0gIlZhbGVuY2lhMSIpCmBgYAoKIyMjIHJhbmdlciBtb2RlbGluZwoKYGBge3J9CiMgY3JlYXRlIFJGIG1vZGVsIHdpdGggcmFuZ2VyIHBhY2thZ2UKcmY1X3JhbmdlciA8LSByYW5nZXIoZm9ybXVsYSA9IGVuZXJneV9kZW1hbmQgfiAuLCBkYXRhID0gd19WTEMpCgpgYGAKCiMjIyBjaGVjayBwZXJmb3JtYW5jZQoKYGBge3J9CiMgY2hlY2sgZXhlY3V0aW9uIHRpbWUKIyBzeXN0ZW0udGltZShyZjVfcmFuZ2VyIDwtIHJhbmdlcihmb3JtdWxhID0gZW5lcmd5X2RlbWFuZCB+IC4sIGRhdGEgPSB3X1ZMQykpCmBgYAoKCmBgYHtyfQojIHBlcmZvcm1hbmNlCnJtc2UoCiAgZV9WTEMkZW5lcmd5X2RlbWFuZCwgCiAgcHJlZGljdChyZjVfcmFuZ2VyLCBlX1ZMQykkcHJlZGljdGlvbnMKICApCnBsb3QoCiAgZV9WTEMkZW5lcmd5X2RlbWFuZCwgCiAgcHJlZGljdChyZjVfcmFuZ2VyLCBlX1ZMQykkcHJlZGljdGlvbnMsIHhsYWIgPSAicHJlZHZhbHVlIiwgbWFpbiA9ICJWYWxlbmNpYTIiCiAgKQpgYGAKCgoKCgoKCg==